<?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: NisaNil</title>
    <description>The latest articles on DEV Community by NisaNil (@gangpt).</description>
    <link>https://dev.to/gangpt</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%2F1223754%2F1f71fa20-cf84-42d4-929f-2ec2eeb17325.png</url>
      <title>DEV Community: NisaNil</title>
      <link>https://dev.to/gangpt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gangpt"/>
    <language>en</language>
    <item>
      <title># Backing Up My Instagram Posts, Stories, and Highlights: Lessons From Rate Limits and Blocks</title>
      <dc:creator>NisaNil</dc:creator>
      <pubDate>Thu, 12 Nov 2026 19:44:33 +0000</pubDate>
      <link>https://dev.to/gangpt/-backing-up-my-instagram-posts-stories-and-highlights-lessons-from-rate-limits-and-blocks-1am6</link>
      <guid>https://dev.to/gangpt/-backing-up-my-instagram-posts-stories-and-highlights-lessons-from-rate-limits-and-blocks-1am6</guid>
      <description>&lt;p&gt;&lt;em&gt;Tags: #python #webscraping #api&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you maintain an Instagram account and want independent backups of your posts, stories, and highlights, this is the kind of problem I ran into.&lt;/p&gt;

&lt;p&gt;I wanted a small, repeatable backup process for my own Instagram content. Nothing particularly sophisticated: retrieve available media, save the original files, preserve useful metadata, and avoid downloading the same content repeatedly.&lt;/p&gt;

&lt;p&gt;The difficult part wasn't saving images or videos. It was keeping the data collection process reliable.&lt;/p&gt;

&lt;p&gt;After running into request limits, access restrictions, and the maintenance requirements of unofficial extraction methods, I started evaluating hosted APIs.&lt;/p&gt;

&lt;p&gt;Here are the technical lessons I took from that process.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Instagram Backups Are Not Just File Downloads
&lt;/h2&gt;

&lt;p&gt;My initial approach was straightforward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Identify the Instagram account.&lt;/li&gt;
&lt;li&gt;Retrieve its published posts.&lt;/li&gt;
&lt;li&gt;Download the available media.&lt;/li&gt;
&lt;li&gt;Collect accessible stories and highlights.&lt;/li&gt;
&lt;li&gt;Store metadata alongside each file.&lt;/li&gt;
&lt;li&gt;Repeat periodically to capture new content.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That sounds simple until different content types enter the picture.&lt;/p&gt;

&lt;p&gt;Regular posts are relatively straightforward because they remain associated with an account unless deleted or archived.&lt;/p&gt;

&lt;p&gt;Stories are more complicated because they are temporary. Highlights introduce another layer because their contents and organization can change.&lt;/p&gt;

&lt;p&gt;A useful backup system therefore needs more than a directory of JPEG and MP4 files.&lt;/p&gt;

&lt;p&gt;I wanted to preserve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Media identifiers&lt;/li&gt;
&lt;li&gt;Publication timestamps&lt;/li&gt;
&lt;li&gt;Captions and available metadata&lt;/li&gt;
&lt;li&gt;Original media URLs when available&lt;/li&gt;
&lt;li&gt;Local filenames and checksums&lt;/li&gt;
&lt;li&gt;Download timestamps&lt;/li&gt;
&lt;li&gt;Information about failed or incomplete downloads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal was to make backups verifiable and repeatable.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Rate Limits Changed the Design
&lt;/h2&gt;

&lt;p&gt;My early approach treated data collection as a sequence of HTTP requests.&lt;/p&gt;

&lt;p&gt;Request a profile, fetch media, download files, and repeat.&lt;/p&gt;

&lt;p&gt;The problem is that Instagram doesn't behave like an unrestricted public data repository.&lt;/p&gt;

&lt;p&gt;Automated access can encounter rate limits, authentication challenges, session expiration, and other restrictions.&lt;/p&gt;

&lt;p&gt;Browser automation also adds maintenance overhead when page structures change.&lt;/p&gt;

&lt;p&gt;Unofficial libraries can simplify some operations, but they don't eliminate platform restrictions.&lt;/p&gt;

&lt;p&gt;I eventually stopped treating failed requests as something to overcome through increasingly aggressive retries.&lt;/p&gt;

&lt;p&gt;Instead, I designed the backup process around bounded requests, caching, explicit failures, and resumable jobs.&lt;/p&gt;

&lt;p&gt;The most important lesson was simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A backup system that repeatedly triggers restrictions is not a reliable backup system.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Why I Started Using a Hosted REST API
&lt;/h2&gt;

&lt;p&gt;I evaluated &lt;a href="https://hikerapi.com/help/instagram-scraping-without-getting-blocked?utm_source=devto&amp;amp;utm_medium=rewards" rel="noopener noreferrer"&gt;HikerAPI&lt;/a&gt; as an alternative to maintaining my own Instagram extraction infrastructure.&lt;/p&gt;

&lt;p&gt;It's a hosted REST API with authentication through an &lt;code&gt;x-access-key&lt;/code&gt; header.&lt;/p&gt;

&lt;p&gt;Advertised pricing starts at $0.001 per request, and 100 free requests are available for testing.&lt;/p&gt;

&lt;p&gt;For my use case, the main advantage was reducing the operational work associated with data extraction.&lt;/p&gt;

&lt;p&gt;That doesn't mean a hosted API guarantees unlimited access.&lt;/p&gt;

&lt;p&gt;Endpoint availability, account permissions, rate limits, media coverage, pricing, and response consistency still need to be checked.&lt;/p&gt;

&lt;p&gt;For backing up my own account, I also consider Instagram's official data export an important option, especially when completeness matters.&lt;/p&gt;

&lt;p&gt;A third-party API is most useful when I need programmable retrieval and ongoing automation that its supported endpoints can provide.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Testing the API With Python
&lt;/h2&gt;

&lt;p&gt;I began with a basic request to inspect account data.&lt;/p&gt;

&lt;p&gt;The following example retrieves a public profile and then requests a chunk of its followers.&lt;/p&gt;

&lt;p&gt;The follower request is included to demonstrate the authentication and request structure. It is &lt;strong&gt;not&lt;/strong&gt; a media-backup endpoint.&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="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"x-access-key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"YOUR_KEY"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;"https://api.hikerapi.com/v2/user/by/username?username=nike"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;"https://api.hikerapi.com/gql/user/followers/chunk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"pk"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&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;This helped establish the basic request pattern.&lt;/p&gt;

&lt;p&gt;For production use, I wouldn't hardcode the API key or assume every response contains the expected fields.&lt;/p&gt;

&lt;p&gt;I would also handle HTTP errors before parsing JSON.&lt;/p&gt;

&lt;p&gt;A more defensive request helper looks like this:&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="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="no"&gt;API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"HIKERAPI_ACCESS_KEY"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"x-access-key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;API_KEY&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;This keeps authentication centralized and makes request handling easier to maintain.&lt;/p&gt;

&lt;p&gt;The actual backup implementation still requires identifying and validating the supported endpoints for posts, stories, and highlights.&lt;/p&gt;

&lt;p&gt;I wouldn't assume that retrieving a profile or its followers proves those media endpoints work.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Making Downloads Resumable
&lt;/h2&gt;

&lt;p&gt;Once media records are available, the next challenge is downloading files safely.&lt;/p&gt;

&lt;p&gt;I wanted downloads to be idempotent: running the same job twice shouldn't create duplicate files.&lt;/p&gt;

&lt;p&gt;For each media record, I would store its identifier and download status in a local database.&lt;/p&gt;

&lt;p&gt;A minimal downloader can use a temporary file before committing a completed download:&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="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_media&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;destination&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;parents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;exist_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;temporary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_suffix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;suffix&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;".part"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;60&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;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temporary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"wb"&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;file&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;chunk&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iter_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;65536&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                        &lt;span class="n"&gt;file&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="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temporary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;temporary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unlink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;missing_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function downloads a supplied media URL. It doesn't discover Instagram media URLs by itself.&lt;/p&gt;

&lt;p&gt;In a complete implementation, I would also validate content types, calculate checksums, and record completed downloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Handling Failures Without Aggressive Retries
&lt;/h2&gt;

&lt;p&gt;I separated failures into categories:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Failure&lt;/th&gt;
&lt;th&gt;Handling strategy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HTTP 429&lt;/td&gt;
&lt;td&gt;Respect &lt;code&gt;Retry-After&lt;/code&gt; and pause collection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP 401 or 403&lt;/td&gt;
&lt;td&gt;Stop and investigate authentication or authorization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP 5xx&lt;/td&gt;
&lt;td&gt;Limited retries with exponential backoff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network timeout&lt;/td&gt;
&lt;td&gt;Retry a bounded number of times&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Missing media&lt;/td&gt;
&lt;td&gt;Record the missing item without inventing a replacement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Invalid response&lt;/td&gt;
&lt;td&gt;Log the error and preserve the original metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I also cache responses when appropriate.&lt;/p&gt;

&lt;p&gt;For a backup job, requesting the same unchanged records repeatedly wastes time and API credits.&lt;/p&gt;

&lt;p&gt;A scheduled process should prioritize newly published content and unresolved failures.&lt;/p&gt;

&lt;p&gt;Stories deserve particular attention because waiting too long can mean the content is no longer retrievable through the available interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Hosted APIs Have Their Own Costs
&lt;/h2&gt;

&lt;p&gt;Moving away from self-managed scraping doesn't remove operational considerations. It changes them.&lt;/p&gt;

&lt;p&gt;With DIY scraping, I would be responsible for maintaining browser automation, handling extraction failures, and managing infrastructure.&lt;/p&gt;

&lt;p&gt;With a hosted API, I depend on an external provider.&lt;/p&gt;

&lt;p&gt;The relevant costs include more than the advertised minimum request price:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Endpoint-specific pricing&lt;/li&gt;
&lt;li&gt;Pagination requirements&lt;/li&gt;
&lt;li&gt;Failed and repeated requests&lt;/li&gt;
&lt;li&gt;Media availability&lt;/li&gt;
&lt;li&gt;Storage and bandwidth&lt;/li&gt;
&lt;li&gt;API changes&lt;/li&gt;
&lt;li&gt;Provider outages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I would measure the real cost per successfully backed-up media item rather than relying exclusively on the minimum per-request rate.&lt;/p&gt;

&lt;p&gt;I would also keep my local backup format independent of the API provider so that switching data sources doesn't require reorganizing every saved file.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. What I Would Do Differently
&lt;/h2&gt;

&lt;p&gt;The biggest change in my approach was separating three responsibilities:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data discovery:&lt;/strong&gt; Determine which posts, stories, and highlights are available.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Media retrieval:&lt;/strong&gt; Download available files and verify their integrity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backup management:&lt;/strong&gt; Track completed work, preserve metadata, and identify missing content.&lt;/p&gt;

&lt;p&gt;This separation makes failures easier to diagnose.&lt;/p&gt;

&lt;p&gt;If an API endpoint changes, the backup database and existing media files remain usable.&lt;/p&gt;

&lt;p&gt;If a download fails, the discovery process doesn't need to restart.&lt;/p&gt;

&lt;p&gt;If a provider becomes unavailable, I can evaluate another authorized data source without discarding the entire backup structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Lessons
&lt;/h2&gt;

&lt;p&gt;My main takeaways are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Don't confuse successful API requests with verified backups.&lt;/li&gt;
&lt;li&gt;Treat rate limits and access restrictions as operational boundaries.&lt;/li&gt;
&lt;li&gt;Store media metadata separately from downloaded files.&lt;/li&gt;
&lt;li&gt;Make backup jobs resumable and idempotent.&lt;/li&gt;
&lt;li&gt;Verify support for posts, stories, and highlights individually.&lt;/li&gt;
&lt;li&gt;Consider official Instagram exports for account-owned content.&lt;/li&gt;
&lt;li&gt;Calculate the real cost per completed backup, not just per API call.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For my own Instagram content, I now prefer a design that can combine official exports with programmable retrieval where appropriate.&lt;/p&gt;

&lt;p&gt;A hosted API reduces some extraction-maintenance work, but it doesn't replace validation, error handling, or a proper backup strategy.&lt;/p&gt;

&lt;p&gt;The practical lesson: &lt;strong&gt;the most important part of an Instagram backup tool isn't how quickly it collects data. It's whether I can trust what it saved when I need to restore something.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>automation</category>
      <category>python</category>
      <category>webscraping</category>
    </item>
  </channel>
</rss>
