<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Faisal Ashfaq</title>
    <description>The latest articles on DEV Community by Faisal Ashfaq (@imfaisii).</description>
    <link>https://dev.to/imfaisii</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4174208%2Fb321357e-f407-4b88-a82c-f1510d84cb0e.jpg</url>
      <title>DEV Community: Faisal Ashfaq</title>
      <link>https://dev.to/imfaisii</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/imfaisii"/>
    <language>en</language>
    <item>
      <title>How to Get a Transcript of Any YouTube Video (4 Free Methods)</title>
      <dc:creator>Faisal Ashfaq</dc:creator>
      <pubDate>Fri, 23 Oct 2026 04:25:26 +0000</pubDate>
      <link>https://dev.to/imfaisii/how-to-get-a-transcript-of-any-youtube-video-4-free-methods-2o4c</link>
      <guid>https://dev.to/imfaisii/how-to-get-a-transcript-of-any-youtube-video-4-free-methods-2o4c</guid>
      <description>&lt;p&gt;Sometimes you don't want to &lt;em&gt;watch&lt;/em&gt; a video, you want to &lt;em&gt;read&lt;/em&gt; it. Maybe you need to quote a conference talk accurately, skim a 90-minute lecture for the one part that matters, or paste a tutorial's steps into your notes.&lt;/p&gt;

&lt;p&gt;Almost every way of getting a YouTube transcript relies on the same thing: the video's &lt;strong&gt;caption track&lt;/strong&gt;. YouTube automatically generates captions for most uploads in supported languages, and many creators upload their own. If a video has a caption track, you can get its text for free. If it doesn't, no "transcript tool" can pull text out of thin air. At that point you need actual speech-to-text, which is a different (and usually paid or compute-heavy) job.&lt;/p&gt;

&lt;p&gt;With that in mind, here are four free methods, from zero-setup to fully scriptable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Method 1: YouTube's built-in transcript panel
&lt;/h2&gt;

&lt;p&gt;YouTube has a transcript viewer built in, and plenty of people don't know it exists.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On desktop:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the video on youtube.com.&lt;/li&gt;
&lt;li&gt;Click the description under the video to expand it (the "...more" link).&lt;/li&gt;
&lt;li&gt;Scroll to the bottom of the description and click &lt;strong&gt;Show transcript&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A transcript panel opens next to the video, with a timestamp beside every line.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can click any line to jump to that moment. The panel's menu lets you toggle timestamps off, and a language selector appears if the video has more than one caption track.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On mobile:&lt;/strong&gt; open the video, tap the description, and look for &lt;strong&gt;Show transcript&lt;/strong&gt; in the same place.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good for:&lt;/strong&gt; quickly reading or searching a single video with no extra tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limitations:&lt;/strong&gt; the text lives inside a scrolling panel. You &lt;em&gt;can&lt;/em&gt; select and copy it, but long transcripts are awkward to copy cleanly, timestamps get mixed into the text, and there's no download button. If you just need to find one quote, it's perfect. If you need a file, read on.&lt;/p&gt;




&lt;h2&gt;
  
  
  Method 2: A free web tool (YouTubeScribe)
&lt;/h2&gt;

&lt;p&gt;If you want a downloadable file, a web-based transcript tool saves you the copy-paste work. I'll use &lt;a href="https://youtubescribe.com" rel="noopener noreferrer"&gt;YouTubeScribe&lt;/a&gt; as the example here. Full disclosure: I work on it, so I'll stick to what it actually does.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Copy the video URL. Regular watch links, Shorts, &lt;code&gt;youtu.be&lt;/code&gt; short links and embed URLs all work.&lt;/li&gt;
&lt;li&gt;Paste it into the box on the homepage and click &lt;strong&gt;Get transcript&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Read the timestamped transcript, toggle timestamps on or off, then copy it or download it as &lt;strong&gt;TXT&lt;/strong&gt; or &lt;strong&gt;SRT&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What's happening under the hood is the same as Method 1: it reads the caption track YouTube already has (auto-generated or uploaded) and formats it. That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It works on &lt;strong&gt;public and unlisted&lt;/strong&gt; videos that have captions, in any language the video has a track for (125+ languages are supported).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private and members-only videos won't work&lt;/strong&gt;, and neither will videos with no caption track. You get a clear "no transcript" result instead of invented text.&lt;/li&gt;
&lt;li&gt;TXT and SRT downloads are free, and you don't need an account for your first transcript.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you specifically need the caption file with its timing intact, for example to re-upload subtitles or load them into a video editor, the &lt;a href="https://youtubescribe.com/tools/subtitle-downloader" rel="noopener noreferrer"&gt;subtitle downloader&lt;/a&gt; saves it as SRT or VTT.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good for:&lt;/strong&gt; getting a clean file in a few seconds without installing anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Other honest options:&lt;/strong&gt; there are several similar free web tools (DownSub, Tactiq's YouTube transcript page, YouTubeToTranscript and others). They all read the same caption track, so the text will be identical. The differences are in formats, limits and how much the interface gets in your way. Try a couple and keep the one you like.&lt;/p&gt;




&lt;h2&gt;
  
  
  Method 3: YouTube Studio (for your own videos)
&lt;/h2&gt;

&lt;p&gt;If the video is &lt;strong&gt;yours&lt;/strong&gt;, you already have direct access to the caption files.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://studio.youtube.com" rel="noopener noreferrer"&gt;YouTube Studio&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Open &lt;strong&gt;Subtitles&lt;/strong&gt; from the left menu and choose the video.&lt;/li&gt;
&lt;li&gt;Next to the language track you want, open the options menu and &lt;strong&gt;download&lt;/strong&gt; the file. You'll usually get a choice of formats such as &lt;code&gt;.srt&lt;/code&gt;, &lt;code&gt;.vtt&lt;/code&gt; or &lt;code&gt;.sbv&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is also the best place to &lt;strong&gt;fix&lt;/strong&gt; auto-generated captions. Edit them in Studio and every later transcript (from any method in this article) will reflect your corrections.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good for:&lt;/strong&gt; creators repurposing their own content into blog posts, show notes or newsletters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limitations:&lt;/strong&gt; only works for videos on channels you manage.&lt;/p&gt;




&lt;h2&gt;
  
  
  Method 4: Command-line and code (for developers)
&lt;/h2&gt;

&lt;p&gt;If you need transcripts for many videos, or want them inside a script or pipeline, use the open-source tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  yt-dlp
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/yt-dlp/yt-dlp" rel="noopener noreferrer"&gt;yt-dlp&lt;/a&gt; is the well-known open-source downloader, and it can fetch just the subtitles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yt-dlp &lt;span class="nt"&gt;--skip-download&lt;/span&gt; &lt;span class="nt"&gt;--write-subs&lt;/span&gt; &lt;span class="nt"&gt;--write-auto-subs&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--sub-langs&lt;/span&gt; &lt;span class="s2"&gt;"en.*"&lt;/span&gt; &lt;span class="nt"&gt;--convert-subs&lt;/span&gt; srt &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://www.youtube.com/watch?v=VIDEO_ID"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--write-subs&lt;/code&gt; grabs uploaded (human) captions; &lt;code&gt;--write-auto-subs&lt;/code&gt; grabs auto-generated ones.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--convert-subs srt&lt;/code&gt; needs &lt;code&gt;ffmpeg&lt;/code&gt; installed.&lt;/li&gt;
&lt;li&gt;It also works on playlists, which is handy for a whole lecture series.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  youtube-transcript-api (Python)
&lt;/h3&gt;

&lt;p&gt;For Python projects, &lt;a href="https://pypi.org/project/youtube-transcript-api/" rel="noopener noreferrer"&gt;youtube-transcript-api&lt;/a&gt; returns the transcript as data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;youtube_transcript_api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;YouTubeTranscriptApi&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;youtube_transcript_api.formatters&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SRTFormatter&lt;/span&gt;

&lt;span class="n"&gt;ytt_api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;YouTubeTranscriptApi&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;transcript&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ytt_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;VIDEO_ID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;languages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;snippet&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;transcript&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;snippet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;7.1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;snippet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;video.srt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SRTFormatter&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;format_transcript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transcript&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Good for:&lt;/strong&gt; batch jobs, research datasets and anything you want to automate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limitations:&lt;/strong&gt; YouTube may rate-limit or block requests, especially from cloud servers and data-centre IPs. Please respect YouTube's Terms of Service and creators' rights. Fetching captions for personal notes or research is very different from republishing someone's content wholesale.&lt;/p&gt;




&lt;h2&gt;
  
  
  Which method should you use?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;You want to…&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Read or search one video right now&lt;/td&gt;
&lt;td&gt;YouTube's transcript panel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get a TXT or SRT file without installing anything&lt;/td&gt;
&lt;td&gt;A web tool like YouTubeScribe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Export or fix captions on your own videos&lt;/td&gt;
&lt;td&gt;YouTube Studio&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Process dozens or hundreds of videos&lt;/td&gt;
&lt;td&gt;yt-dlp or youtube-transcript-api&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  A note on accuracy
&lt;/h2&gt;

&lt;p&gt;Auto-generated captions are good, but they're not perfect. They struggle with names, jargon, heavy accents, crosstalk and music. Before you quote someone in an article, paper or social post, &lt;strong&gt;click back to the timestamp and listen&lt;/strong&gt; to make sure the words are right. A transcript is a fantastic index into a video, but it doesn't replace the video itself.&lt;/p&gt;

&lt;p&gt;And if a video has no captions at all, none of the above will help. You'd need a speech-to-text model (for example, running OpenAI's open-source Whisper locally on audio you have the right to use).&lt;/p&gt;

&lt;p&gt;Happy reading, and enjoy getting your evenings back from 90-minute videos.&lt;/p&gt;

</description>
      <category>youtube</category>
      <category>productivity</category>
      <category>tutorial</category>
      <category>python</category>
    </item>
    <item>
      <title>How to Use Grok Models in Claude Code, Cline, Aider and opencode via an OpenAI-Compatible Endpoint</title>
      <dc:creator>Faisal Ashfaq</dc:creator>
      <pubDate>Sun, 18 Oct 2026 05:12:03 +0000</pubDate>
      <link>https://dev.to/imfaisii/how-to-use-grok-models-in-claude-code-cline-aider-and-opencode-via-an-openai-compatible-endpoint-2md</link>
      <guid>https://dev.to/imfaisii/how-to-use-grok-models-in-claude-code-cline-aider-and-opencode-via-an-openai-compatible-endpoint-2md</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; I build Grokified, the gateway used as the worked example here. The patterns below work with any OpenAI-compatible endpoint. Swap in your own base URL, key and model id.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most AI coding agents let you bring your own model provider now. The settings screens look different, but under the hood nearly all of them ask the same three questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Base URL:&lt;/strong&gt; where should requests go?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API key:&lt;/strong&gt; what goes in the &lt;code&gt;Authorization: Bearer&lt;/code&gt; header?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model id:&lt;/strong&gt; what string goes in the &lt;code&gt;model&lt;/code&gt; field?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once you can answer those three, you can point Cline, Aider, opencode and (with one extra step) Claude Code at almost any OpenAI-compatible backend. This post walks through each tool with real values, then covers the one case that doesn't fit the pattern.&lt;/p&gt;

&lt;p&gt;My examples use these values from the &lt;a href="https://grokified.com/docs" rel="noopener noreferrer"&gt;Grokified docs&lt;/a&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Base URL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://api.grokified.com/v1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Key&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;gk_live_...&lt;/code&gt; (production) or &lt;code&gt;gk_test_...&lt;/code&gt; (test)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model&lt;/td&gt;
&lt;td&gt;&lt;code&gt;grok-4.7&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 0: Prove the endpoint works before touching any agent
&lt;/h2&gt;

&lt;p&gt;Don't debug three layers at once. Before configuring an editor, send the smallest possible request with cURL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.grokified.com/v1/chat/completions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$GROKIFIED_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "model": "grok-4.7",
    "messages": [{"role": "user", "content": "Hello"}]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If that returns &lt;code&gt;200&lt;/code&gt;, your key, base URL and model id are all good, so any later failure is the agent's config. If it doesn't, the status code tells you where to look:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;401&lt;/code&gt;: wrong or revoked key, or a missing &lt;code&gt;Bearer&lt;/code&gt; prefix&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;402&lt;/code&gt;: balance or billing on the account&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;403&lt;/code&gt;: environment or permission mismatch (e.g. a test key against production)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;400&lt;/code&gt;/&lt;code&gt;404&lt;/code&gt; on the model: the model id isn't current&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;429&lt;/code&gt;/&lt;code&gt;5xx&lt;/code&gt;: the only codes worth retrying&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point matters for agents. Retrying a &lt;code&gt;401&lt;/code&gt; in a loop just wastes time. Fix the config first.&lt;/p&gt;

&lt;p&gt;The same check in Python, using the OpenAI SDK unchanged except for the host:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gk_live_...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.grokified.com/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The point of "OpenAI-compatible" is that the request shape stays the same. Only the host line moves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cline (and Roo Code / Kilo Code)
&lt;/h2&gt;

&lt;p&gt;Cline is a VS Code agent extension, so the config lives in its own settings panel rather than a file. Pick the OpenAI-compatible provider and fill in four fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API Provider: OpenAI Compatible
Base URL: https://api.grokified.com/v1
API Key: gk_live_...
Model ID: grok-4.7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Roo Code and Kilo Code have the same "custom provider" slot with the same fields.&lt;/p&gt;

&lt;p&gt;Two practical notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model availability can depend on your plan.&lt;/strong&gt; With Grokified, &lt;code&gt;grok-4.7&lt;/code&gt; needs the Basic plan or above, and the docs suggest &lt;code&gt;grok-4.6&lt;/code&gt; otherwise. Other providers have similar gating, so if the model returns an error and cURL with a different id works, this is probably why.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The key goes into VS Code's encrypted key storage&lt;/strong&gt;, not a plaintext file in your repo. That's the right place for it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Aider
&lt;/h2&gt;

&lt;p&gt;Aider reads the standard OpenAI environment variables, and the &lt;code&gt;openai/&lt;/code&gt; prefix on the model name tells it to use the OpenAI-compatible client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENAI_API_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://api.grokified.com/v1
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gk_live_...
aider &lt;span class="nt"&gt;--model&lt;/span&gt; openai/grok-4.7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you'd rather not export a key into every shell, Aider also reads &lt;code&gt;~/.aider.conf.yml&lt;/code&gt;. Keep the key itself in an environment variable rather than the YAML file, especially if you sync dotfiles.&lt;/p&gt;

&lt;h2&gt;
  
  
  opencode
&lt;/h2&gt;

&lt;p&gt;opencode is an open-source terminal agent that supports custom compatible providers. Its config lives at &lt;code&gt;~/.config/opencode/opencode.json&lt;/code&gt;. You register a provider with the base URL, read the key from an environment variable, and add &lt;code&gt;grok-4.7&lt;/code&gt; as a model. Check opencode's own provider docs for the exact JSON schema, since it changes between versions. The three values are the same as everywhere else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Claude Code: the one that's different
&lt;/h2&gt;

&lt;p&gt;Claude Code speaks &lt;strong&gt;Anthropic's&lt;/strong&gt; Messages protocol, not OpenAI's chat completions. Setting a base URL alone won't work against an OpenAI-shaped endpoint, because the request bodies are different.&lt;/p&gt;

&lt;p&gt;You have two options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use a provider that exposes an Anthropic-shaped endpoint directly.&lt;/li&gt;
&lt;li&gt;Run a small local translator that accepts Anthropic-format requests and forwards OpenAI-format ones.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Grokified takes the second approach. Its CLI starts a local relay on &lt;code&gt;127.0.0.1:4111&lt;/code&gt;, and Claude Code's &lt;code&gt;~/.claude/settings.json&lt;/code&gt; points there. The key is held by the relay process rather than written into the settings file. You can manage the relay directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;grokified relay status
grokified relay stop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If something else is already on port 4111, those two commands are your first stop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing all of the above in one command
&lt;/h2&gt;

&lt;p&gt;Hand-editing four configs is fine once. Doing it on every new machine gets old, which is why I built a CLI for it. It's specific to Grokified, but here's what it does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; grokified
grokified login      &lt;span class="c"&gt;# browser sign-in; or: grokified login --key gk_live_... for headless&lt;/span&gt;
grokified setup      &lt;span class="c"&gt;# opens a picker of tools it can configure&lt;/span&gt;
grokified doctor     &lt;span class="c"&gt;# checks key, balance, relay, and what's wired up&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also target one tool at a time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;grokified setup claude
grokified setup aider
grokified setup opencode
grokified setup vscode-agents   &lt;span class="c"&gt;# Cline / Roo Code / Kilo Code&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The design choice I care most about is that &lt;strong&gt;the key is never copied into any tool's own config file&lt;/strong&gt;. It's stored once in &lt;code&gt;~/.grokified/credentials&lt;/code&gt;, and tools get it as an environment variable at launch (opencode, Aider), through the relay (Claude Code), or through the extension's encrypted storage (Cline). That means your dotfiles repo, screen shares and &lt;code&gt;grep -r&lt;/code&gt; of your home directory don't leak it.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://grokified.com/cli" rel="noopener noreferrer"&gt;CLI page&lt;/a&gt; lists every config file it touches.&lt;/p&gt;

&lt;p&gt;Troubleshooting notes from the docs that apply to most npm-installed CLIs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;command not found&lt;/code&gt; after install usually means npm's global bin isn't on your &lt;code&gt;PATH&lt;/code&gt;: &lt;code&gt;export PATH="$(npm config get prefix)/bin:$PATH"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The CLI needs Node 18.17 or newer. The shell installer (&lt;code&gt;curl -fsSL https://grokified.com/install.sh | bash&lt;/code&gt;) sets up a private copy of Node if yours is older.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What this can't reach
&lt;/h2&gt;

&lt;p&gt;Be honest about the limits. Some tools can't be pointed at a custom endpoint at all:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemini CLI&lt;/strong&gt; only speaks Google's &lt;code&gt;generateContent&lt;/code&gt; format. Changing its base URL changes where requests go, not what they look like.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Copilot&lt;/strong&gt; has no custom connection setting for this.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cursor's&lt;/strong&gt; built-in model picker is closed for this flow, but extensions like Cline running &lt;em&gt;inside&lt;/em&gt; Cursor can still use a custom provider.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A note on cost
&lt;/h2&gt;

&lt;p&gt;I won't pretend there's no commercial angle: Grokified bills Grok usage at a discount to list price, and the details are on the &lt;a href="https://grokified.com/pricing" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt;. The more general lesson is that agents resend a lot of context on every turn, so &lt;strong&gt;prompt caching matters more than headline per-token prices&lt;/strong&gt; for agent workloads. Whatever provider you use, check whether repeated prefixes are cached automatically or need opting in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Verify the endpoint with one cURL call before configuring anything.&lt;/li&gt;
&lt;li&gt;Cline, Aider and opencode each need just a base URL, a key and a model id.&lt;/li&gt;
&lt;li&gt;Claude Code needs an Anthropic-shaped endpoint or a local relay.&lt;/li&gt;
&lt;li&gt;Keep keys out of config files: use env vars or encrypted storage.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you try this with another OpenAI-compatible provider and hit a quirk, I'd like to hear about it in the comments.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>devtools</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building a council-level dataset of UK ECO home energy upgrades</title>
      <dc:creator>Faisal Ashfaq</dc:creator>
      <pubDate>Fri, 09 Oct 2026 20:46:04 +0000</pubDate>
      <link>https://dev.to/imfaisii/building-a-council-level-dataset-of-uk-eco-home-energy-upgrades-418j</link>
      <guid>https://dev.to/imfaisii/building-a-council-level-dataset-of-uk-eco-home-energy-upgrades-418j</guid>
      <description>&lt;p&gt;Official UK statistics are often published as multi-sheet Excel workbooks. This post covers how I turned the DESNZ ECO tables into one clean CSV with a row per council, and what it shows.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/imfaisii/eco-measures-by-council-gb" rel="noopener noreferrer"&gt;https://github.com/imfaisii/eco-measures-by-council-gb&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Source and licence
&lt;/h2&gt;

&lt;p&gt;Department for Energy Security and Net Zero (DESNZ), &lt;em&gt;Household Energy Efficiency Statistics&lt;/em&gt;, headline release June 2026 (published 25 June 2026): Table 3.4 (ECO measures by administrative area), Table 3.5 (Affordable Warmth measures via flexible eligibility) and Table 4.4 (households in receipt of ECO measures). Contains public sector information licensed under the Open Government Licence v3.0. Underlying data (c) Crown copyright.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cleaning and joining
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Read the three tables with pandas and joined them on the ONS/GSS area code (E06, E07, E08, E09, W06, S12).&lt;/li&gt;
&lt;li&gt;Dropped the county and Inner/Outer London sub-total rows to avoid double counting. The county is kept in its own column.&lt;/li&gt;
&lt;li&gt;Kept values as published. Nothing was adjusted or estimated.&lt;/li&gt;
&lt;li&gt;Kept DESNZ suppression markers: &lt;code&gt;#&lt;/code&gt; means 1 to 4 withheld, &lt;code&gt;^&lt;/code&gt; means 5 or more withheld. The value is left blank and the marker goes in a flag column.&lt;/li&gt;
&lt;li&gt;Check: the 350 council rows sum exactly to the published GB totals (4,453,004 measures, 2,595,218 households).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Columns
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Column&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;council&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Local authority name as published&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;area_code&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ONS/GSS code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;county_or_met_county&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;County, metropolitan county or London sub-area; blank for unitary authorities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;region_or_nation&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;English region, Wales or Scotland&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;eco_measures_jan2013_mar2026&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All ECO phases, Jan 2013 to Mar 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;measures_per_1000_households&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DESNZ published rate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;households_helped_jan2013_mar2026&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Households with at least one measure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;eco3_interim_eco4_flex_measures&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Flexible eligibility measures since April 2022; may be blank&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;flex_suppressed_flag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;#&lt;/code&gt; or &lt;code&gt;^&lt;/code&gt; where withheld&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Findings
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Compare councils by rate, not totals. Birmingham has the most measures (195,919), but Bradford has the highest rate at 578.4 per 1,000 households, just ahead of Ceredigion (577.7).&lt;/li&gt;
&lt;li&gt;By region, the North West leads at 243.2 per 1,000 households, while London (73.4) and the South East (76.8) are under half the GB rate of 156.7.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Caveats: one household can get several measures, the totals cover every ECO phase since 2013, and the figures are provisional.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full table
&lt;/h2&gt;

&lt;p&gt;The full table with methodology is at &lt;a href="https://applyfreeboiler.uk/data/eco-measures-by-council" rel="noopener noreferrer"&gt;ECO measures by council&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>opendata</category>
      <category>python</category>
      <category>datascience</category>
      <category>uk</category>
    </item>
  </channel>
</rss>
