<?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: devhop</title>
    <description>The latest articles on DEV Community by devhop (@p3nt_55db490b33574db3c3e3).</description>
    <link>https://dev.to/p3nt_55db490b33574db3c3e3</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%2F3938572%2F98a1c79d-547d-4656-b306-b3521b6e16c4.png</url>
      <title>DEV Community: devhop</title>
      <link>https://dev.to/p3nt_55db490b33574db3c3e3</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/p3nt_55db490b33574db3c3e3"/>
    <language>en</language>
    <item>
      <title>Building an eSIM Provisioning Flow, From Order to Installed Profile</title>
      <dc:creator>devhop</dc:creator>
      <pubDate>Sun, 06 Sep 2026 19:40:30 +0000</pubDate>
      <link>https://dev.to/p3nt_55db490b33574db3c3e3/h-22kn</link>
      <guid>https://dev.to/p3nt_55db490b33574db3c3e3/h-22kn</guid>
      <description>&lt;p&gt;If you've ever bought a travel data plan and had it working before the plane landed, you've used eSIM provisioning. From the outside it looks like magic: scan a QR code, tap "Add", done. From the inside it's a surprisingly well specified pipeline that any developer can plug into.&lt;/p&gt;

&lt;p&gt;This post walks through how a consumer eSIM provisioning flow actually works, what the moving parts are, and how you'd design one in your own product whether that's a travel app, an IoT dashboard, or an internal tool for issuing connectivity to a distributed team.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three actors you need to know
&lt;/h2&gt;

&lt;p&gt;Everything in consumer eSIM is defined by the GSMA &lt;strong&gt;SGP.22&lt;/strong&gt; specification (the "Remote SIM Provisioning" architecture for consumer devices). Three components matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;eUICC&lt;/strong&gt;: the embedded chip inside the phone, tablet or laptop. It can hold multiple carrier profiles and switch between them. Basically a small, secure SIM that can hold several carriers at once.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SM-DP+&lt;/strong&gt; (Subscription Manager Data Preparation, "plus")  the carrier-side server that stores prepared profiles and hands them out. This is the endpoint the device talks to when downloading a plan.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LPA&lt;/strong&gt; (Local Profile Assistant) the client on the device that speaks to the SM-DP+, verifies certificates, downloads the profile and installs it on the eUICC. On iOS and Android this is built into the OS.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The whole flow: &lt;strong&gt;your backend asks a provider to prepare a profile, the provider's SM-DP+ holds it, the device's LPA fetches it, and the eUICC installs it.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's actually inside a QR code
&lt;/h2&gt;

&lt;p&gt;The thing the user scans is just a string with a fixed format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LPA:1$smdp.example.com$ABC123-MATCHING-ID
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three fields, &lt;code&gt;$&lt;/code&gt;-separated:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;LPA:1&lt;/code&gt;, the format version.&lt;/li&gt;
&lt;li&gt;The SM-DP+ address the device should contact.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;matching ID&lt;/strong&gt;, a one-time token that identifies the prepared profile waiting for this user.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There is no secret key in the QR code. The security comes from mutual TLS and certificate chains between the LPA and SM-DP+, rooted in the GSMA CI (Certificate Issuer). The matching ID is essentially a claim ticket.&lt;/p&gt;

&lt;p&gt;Practical consequence for you as a developer: &lt;strong&gt;you don't need to render a QR image server-side.&lt;/strong&gt; Ship the activation string and let the client render it, or better, on modern devices use the OS deep-link / universal-link installers that consume the same string without a camera.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing the provisioning flow
&lt;/h2&gt;

&lt;p&gt;Here's the architecture most eSIM-enabled products converge on. Nothing unusual here, it's a normal order, fulfilment and delivery pipeline with a telecom step in the middle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────┐   1. order    ┌──────────────┐   2. create   ┌──────────────┐
│  Client  │ ────────────► │ Your backend │ ────────────► │ eSIM provider│
│ (web/app)│               │              │               │   (API)      │
└──────────┘               └──────────────┘               └──────┬───────┘
      ▲                           │                              │
      │ 4. activation code        │ 3. webhook: profile ready    │
      │    + install button       │◄─────────────────────────────┘
      │                           ▼
      └───────────────────── deliver (in-app, email, SMS)

┌──────────┐   5. LPA fetches profile   ┌──────────────┐
│  Device  │ ─────────────────────────► │   SM-DP+     │
└──────────┘                            └──────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 1: Model the product, not the SIM
&lt;/h3&gt;

&lt;p&gt;Your catalogue entity is a &lt;em&gt;plan&lt;/em&gt;: region or country coverage, data allowance, validity window, whether it's data-only, price. The eSIM itself is a fulfilment artefact, not something the user browses. Keep them separate in your schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;coverage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;      &lt;span class="c1"&gt;// ISO country codes&lt;/span&gt;
  &lt;span class="nl"&gt;dataMb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;validityDays&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;dataOnly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Esim&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;planId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;iccid&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;          &lt;span class="c1"&gt;// known after provisioning&lt;/span&gt;
  &lt;span class="nl"&gt;activationCode&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// LPA:1$...&lt;/span&gt;
  &lt;span class="nl"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pending&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ready&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;installed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;expired&lt;/span&gt;&lt;span class="dl"&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;h3&gt;
  
  
  Step 2: Provision asynchronously
&lt;/h3&gt;

&lt;p&gt;Almost every eSIM provider exposes a REST API where you &lt;code&gt;POST&lt;/code&gt; an order and get back an eSIM record with an activation code. Some return it synchronously, many finalise it via webhook a few seconds later. &lt;strong&gt;Design for the async case from day one&lt;/strong&gt;, even if your first provider happens to be synchronous.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pseudo-code, provider-agnostic&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createEsim&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;planId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;externalRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;orderId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;esims&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;planId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activationCode&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ready&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pending&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;activationCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activationCode&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;And the webhook handler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/webhooks/esim&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;verifySignature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;esimId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;iccid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;activationCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;profile.ready&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;esims&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="nx"&gt;esimId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;iccid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;activationCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ready&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;notifyUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;esimId&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;profile.installed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;esims&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="nx"&gt;esimId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;installed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&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;Two things that bite people here: make the handler &lt;strong&gt;idempotent&lt;/strong&gt; (providers retry), and verify the webhook signature before trusting anything in the body.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Deliver the activation code the right way
&lt;/h3&gt;

&lt;p&gt;You have three delivery options, in decreasing order of user friction:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;QR code&lt;/strong&gt;. Universal, but requires a &lt;em&gt;second&lt;/em&gt; device to scan from (you can't scan your own screen). Fine for desktop purchases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual entry&lt;/strong&gt;. Show the SM-DP+ address and matching ID separately. Painful, but it's the fallback that always works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One-tap install&lt;/strong&gt;. On iOS 17.4+ you can open a universal link of the form &lt;code&gt;https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA:1$...&lt;/code&gt;, and Android supports an &lt;code&gt;LPA:&lt;/code&gt; intent handled by the system. This removes the camera entirely.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Detect the platform and default to option 3, keep 1 and 2 visible as fallbacks. Whatever you do, &lt;strong&gt;also email the code&lt;/strong&gt;, because users install eSIMs at airports with 4% battery and no patience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Track lifecycle, not just delivery
&lt;/h3&gt;

&lt;p&gt;An eSIM has a lifecycle beyond "sent": installed on device, activated on first network attach, consuming data, then expired or exhausted. Providers expose usage endpoints (typically remaining data + expiry). Poll them or subscribe to usage webhooks and surface the numbers in your UI. This is where most of the support tickets come from, so invest here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotchas from the field
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One activation code, one install.&lt;/strong&gt; Once a profile is downloaded, the matching ID is consumed. If the user deletes the profile from their phone, it's gone and you'll need to issue a new eSIM. Warn users loudly before they delete.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Device compatibility isn't binary.&lt;/strong&gt; Some phones have eSIM hardware but are carrier-locked. Some regional variants of a model lack eSIM entirely. Ship a compatibility checker based on the model, but tell the user it's a best guess.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data-only plans have no phone number.&lt;/strong&gt; Users expect to receive SMS 2FA codes on their new eSIM. Set expectations in the product copy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Roaming is not local.&lt;/strong&gt; Many travel eSIMs are technically roaming on a partner network, which affects speed caps and 5G availability. Show the underlying network operator per country when the provider gives you that data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;APN settings.&lt;/strong&gt; Most profiles configure the APN automatically, but not all. Keep the APN in your delivery email as a fallback.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A minimal end-to-end checklist
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Plan catalogue with coverage, allowance, validity.&lt;/li&gt;
&lt;li&gt;Order comes in, you call the provider API and store a &lt;code&gt;pending&lt;/code&gt; eSIM.&lt;/li&gt;
&lt;li&gt;Webhook handler (idempotent, signature-verified) flips it to &lt;code&gt;ready&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Platform-aware delivery: one-tap link, QR, manual, plus email.&lt;/li&gt;
&lt;li&gt;Usage polling or webhooks to show remaining data and expiry.&lt;/li&gt;
&lt;li&gt;Support tooling: look up an eSIM by ICCID, re-send activation details, see install status.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you can build a webhook-driven order pipeline, you can build an eSIM product. The telecom part is abstracted away behind the SM-DP+. Your job is the state machine and the UX around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.gsma.com/solutions-and-impact/technologies/esim/gsma_resources/sgp-22-v3-1/" rel="noopener noreferrer"&gt;GSMA SGP.22 RSP Technical Specification&lt;/a&gt;: the consumer eSIM spec. The section on the activation code format is the one to read first.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.gsma.com/solutions-and-impact/technologies/esim/" rel="noopener noreferrer"&gt;GSMA eSIM overview&lt;/a&gt;: high-level architecture and terminology.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.android.com/reference/android/telephony/euicc/EuiccManager" rel="noopener noreferrer"&gt;Android EuiccManager&lt;/a&gt;: the Android API surface for LPA interactions if you're building a native client.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://esimcenter.com" rel="noopener noreferrer"&gt;eSIM Center&lt;/a&gt;: a live example of a consumer travel eSIM product built on this exact flow: plan catalogue by country/region, instant activation-code delivery, and usage tracking. Useful to click through if you want to see how the UX decisions above look in production.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Have you shipped an eSIM integration? I'd be curious which provider APIs you found easiest to work with and what surprised you, drop it in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>esim</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>backend</category>
    </item>
    <item>
      <title>Instagram Graph API ile Takipçi Sayısı ve Hesap Metriklerini Çekmek (Node.js Rehberi)</title>
      <dc:creator>devhop</dc:creator>
      <pubDate>Mon, 18 May 2026 16:52:58 +0000</pubDate>
      <link>https://dev.to/p3nt_55db490b33574db3c3e3/instagram-graph-api-ile-takipci-sayisi-ve-hesap-metriklerini-cekmek-nodejs-rehberi-3e1</link>
      <guid>https://dev.to/p3nt_55db490b33574db3c3e3/instagram-graph-api-ile-takipci-sayisi-ve-hesap-metriklerini-cekmek-nodejs-rehberi-3e1</guid>
      <description>&lt;p&gt;Sosyal medya analitiği yapan bir araç, içerik zamanlama uygulaması ya da kişisel bir dashboard yazıyorsanız Instagram'ın &lt;strong&gt;resmi&lt;/strong&gt; API'sini kullanmanız gerekir: &lt;strong&gt;Instagram Graph API&lt;/strong&gt;. Bu yazıda sıfırdan kurulum, kimlik doğrulama ve takipçi sayısı / post insights çekme adımlarını Node.js ile anlatacağım.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Önemli not: "Instagram Basic Display API" Aralık 2024'te kapatıldı. Aktif olan tek resmi yol &lt;strong&gt;Instagram Graph API&lt;/strong&gt;'dir ve &lt;strong&gt;Business&lt;/strong&gt; veya &lt;strong&gt;Creator&lt;/strong&gt; hesabı ile bir &lt;strong&gt;Facebook Page&lt;/strong&gt; bağlantısı gerektirir.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Neden Resmi API?
&lt;/h2&gt;

&lt;p&gt;Tersine mühendislikle çıkarılmış "private API" veya tarayıcı kazıma (scraping) yöntemleri kısa vadede çalışsa da uzun vadede üç sorun çıkarır:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instagram'ın algoritması bot davranışını giderek daha agresif tespit ediyor ve hesabınızı askıya alıyor.&lt;/li&gt;
&lt;li&gt;Yapı her güncellemede değiştiği için kodunuz sürekli kırılır.&lt;/li&gt;
&lt;li&gt;Meta Kullanım Koşulları'nı ihlal eder; ticari bir ürün geliştiriyorsanız hukuki risk taşır.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Resmi Graph API yavaş kurulur ama bir kez kurulduğunda istikrarlı, oran sınırları belli ve dokümante edilmiştir.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ön Koşullar
&lt;/h2&gt;

&lt;p&gt;Başlamadan önce şunlara ihtiyacınız var:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Instagram &lt;strong&gt;Business&lt;/strong&gt; veya &lt;strong&gt;Creator&lt;/strong&gt; hesabı (kişisel hesap çalışmaz — Settings → Account → Switch to Professional Account)&lt;/li&gt;
&lt;li&gt;Instagram hesabına bağlı bir &lt;strong&gt;Facebook Page&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developers.facebook.com/" rel="noopener noreferrer"&gt;Meta for Developers&lt;/a&gt; hesabı&lt;/li&gt;
&lt;li&gt;Node.js 18+ kurulu bir geliştirme ortamı&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. Adım: Meta Developer Uygulaması Oluşturmak
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;developers.facebook.com&lt;/code&gt; adresine girip "My Apps" → "Create App" deyin. Uygulama tipi olarak &lt;strong&gt;Business&lt;/strong&gt; seçin. Uygulama oluştuktan sonra Dashboard'da sol menüden &lt;strong&gt;"Add Product"&lt;/strong&gt; kısmında &lt;strong&gt;Instagram Graph API&lt;/strong&gt;'yi ve &lt;strong&gt;Facebook Login&lt;/strong&gt;'u ekleyin.&lt;/p&gt;

&lt;p&gt;App Settings → Basic kısmından &lt;strong&gt;App ID&lt;/strong&gt; ve &lt;strong&gt;App Secret&lt;/strong&gt; değerlerini bir kenara not edin. Bunları kodda environment variable olarak kullanacaksınız.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Adım: Erişim Token'ı Almak
&lt;/h2&gt;

&lt;p&gt;Instagram Graph API üç token türü kullanır:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Token Tipi&lt;/th&gt;
&lt;th&gt;Geçerlilik&lt;/th&gt;
&lt;th&gt;Kullanım&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Short-lived User Token&lt;/td&gt;
&lt;td&gt;1 saat&lt;/td&gt;
&lt;td&gt;İlk OAuth akışında alınır&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long-lived User Token&lt;/td&gt;
&lt;td&gt;60 gün&lt;/td&gt;
&lt;td&gt;Üretimde kullanılan token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Page Access Token&lt;/td&gt;
&lt;td&gt;Süresiz*&lt;/td&gt;
&lt;td&gt;Sayfa adına Instagram işlemleri için&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;* Page token'ı kullanıcı sayfa yetkilerini değiştirmedikçe geçerli kalır.&lt;/p&gt;

&lt;h3&gt;
  
  
  OAuth Akışı
&lt;/h3&gt;

&lt;p&gt;Test için en hızlısı &lt;strong&gt;Graph API Explorer&lt;/strong&gt;'ı kullanmaktır: &lt;a href="https://developers.facebook.com/tools/explorer/" rel="noopener noreferrer"&gt;https://developers.facebook.com/tools/explorer/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Üretim için kendi OAuth akışınızı kurarsınız:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1. Kullanıcıyı Facebook login'e yönlendir&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loginUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://www.facebook.com/v21.0/dialog/oauth?`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
  &lt;span class="s2"&gt;`client_id=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;APP_ID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
  &lt;span class="s2"&gt;`&amp;amp;redirect_uri=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;REDIRECT_URI&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
  &lt;span class="s2"&gt;`&amp;amp;scope=instagram_basic,instagram_manage_insights,pages_show_list,pages_read_engagement`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
  &lt;span class="s2"&gt;`&amp;amp;response_type=code`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Geri dönüşte code'u short-lived token'a çevir&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;exchangeCodeForToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://graph.facebook.com/v21.0/oauth/access_token?`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s2"&gt;`client_id=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;APP_ID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s2"&gt;`&amp;amp;client_secret=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;APP_SECRET&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s2"&gt;`&amp;amp;redirect_uri=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;REDIRECT_URI&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s2"&gt;`&amp;amp;code=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&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="c1"&gt;// 3. Short-lived token'ı 60 günlük long-lived token'a çevir&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getLongLivedToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shortToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://graph.facebook.com/v21.0/oauth/access_token?`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s2"&gt;`grant_type=fb_exchange_token`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s2"&gt;`&amp;amp;client_id=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;APP_ID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s2"&gt;`&amp;amp;client_secret=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;APP_SECRET&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s2"&gt;`&amp;amp;fb_exchange_token=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;shortToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&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;h2&gt;
  
  
  3. Adım: Instagram Business Account ID'sini Bulmak
&lt;/h2&gt;

&lt;p&gt;Bir kullanıcının Instagram verilerine erişmek için önce &lt;strong&gt;Facebook Page ID&lt;/strong&gt;'sini, sonra ona bağlı &lt;strong&gt;Instagram Business Account ID&lt;/strong&gt;'sini bulmanız gerekir:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getInstagramAccountId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userAccessToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pagesRes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`https://graph.facebook.com/v21.0/me/accounts?access_token=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userAccessToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pagesRes&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pageId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pageToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;igRes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`https://graph.facebook.com/v21.0/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pageId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?fields=instagram_business_account&amp;amp;access_token=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pageToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;igData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;igRes&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;instagramAccountId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;igData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instagram_business_account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;pageAccessToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pageToken&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;h2&gt;
  
  
  4. Adım: Takipçi Sayısı ve Profil Bilgilerini Çekmek
&lt;/h2&gt;

&lt;p&gt;Asıl olay burada başlıyor. Tek bir istekle username, takipçi sayısı, post sayısı gibi temel bilgileri alabilirsiniz:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getAccountInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;igAccountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pageToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;biography&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;followers_count&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;follows_count&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;media_count&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;profile_picture_url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://graph.facebook.com/v21.0/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;igAccountId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s2"&gt;`?fields=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;access_token=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pageToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&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="c1"&gt;// Örnek çıktı:&lt;/span&gt;
&lt;span class="c1"&gt;// {&lt;/span&gt;
&lt;span class="c1"&gt;//   username: "ornek_hesap",&lt;/span&gt;
&lt;span class="c1"&gt;//   followers_count: 12453,&lt;/span&gt;
&lt;span class="c1"&gt;//   follows_count: 188,&lt;/span&gt;
&lt;span class="c1"&gt;//   media_count: 247&lt;/span&gt;
&lt;span class="c1"&gt;// }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Adım: Post Insights (Etkileşim Verisi)
&lt;/h2&gt;

&lt;p&gt;Belirli bir gönderinin beğeni, yorum, erişim ve gösterim sayılarını almak için:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getMediaList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;igAccountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pageToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://graph.facebook.com/v21.0/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;igAccountId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/media`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s2"&gt;`?fields=id,caption,media_type,permalink,timestamp,like_count,comments_count`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s2"&gt;`&amp;amp;access_token=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pageToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getMediaInsights&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mediaId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pageToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metrics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;impressions,reach,saved,engagement&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://graph.facebook.com/v21.0/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;mediaId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/insights`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s2"&gt;`?metric=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;access_token=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pageToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&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;h2&gt;
  
  
  6. Adım: Hesap Bazında Insights
&lt;/h2&gt;

&lt;p&gt;Hesabın genelinde günlük/haftalık erişim, profil ziyareti gibi metrikler için:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getAccountInsights&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;igAccountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pageToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metrics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;impressions,reach,profile_views&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;period&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;day&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://graph.facebook.com/v21.0/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;igAccountId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/insights`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s2"&gt;`?metric=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;period=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;period&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;access_token=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pageToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&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;h2&gt;
  
  
  Hız Sınırları (Rate Limits)
&lt;/h2&gt;

&lt;p&gt;Instagram Graph API uygulama bazında &lt;strong&gt;saatte 200 çağrı × hesap sayısı&lt;/strong&gt; olarak hesaplanır. Yani 100 işletme hesabınız varsa saatte 20.000 çağrı yapabilirsiniz. Hız sınırını aştığınızda &lt;code&gt;error.code === 4&lt;/code&gt; yanıtı dönüyor.&lt;/p&gt;

&lt;p&gt;İyi pratikler:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sık çağırdığınız verileri (örn. takipçi sayısı) en az 15 dakika cache'leyin&lt;/li&gt;
&lt;li&gt;Insight çağrılarını günde bir kez batch olarak çalıştırın&lt;/li&gt;
&lt;li&gt;Webhook'larla (&lt;code&gt;instagram_manage_insights&lt;/code&gt;) push tabanlı akışa geçin&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sık Karşılaşılan Hatalar
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;(#10) Application does not have permission for this action&lt;/code&gt;&lt;/strong&gt; — App Review'dan geçmeyen permission istiyorsunuz. Test için kendinizi geliştirici/test kullanıcısı olarak ekleyin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;Invalid OAuth access token&lt;/code&gt;&lt;/strong&gt; — Token süresi dolmuş ya da farklı uygulamaya ait. Long-lived token'ı 50. günde proaktif olarak yenileyin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;Instagram User does not exist&lt;/code&gt;&lt;/strong&gt; — Hesap Business/Creator değil ya da Page'e bağlı değil.&lt;/p&gt;

&lt;p&gt;Instagram'ın resmi API'si ilk kurulumda epey kalabalık (Facebook App + Page + Business hesabı + OAuth) ama bir kez ayaklandıktan sonra sağlam bir temel oluşturur. Üzerine; planlanmış yayın aracı, analytics dashboard, içerik öneri sistemi, otomatik DM yanıtlayıcı gibi pek çok proje kurabilirsiniz.&lt;/p&gt;

&lt;p&gt;Eğer scraping ya da bot otomasyonu düşünüyorsanız: kısa vadeli kazançtan vazgeçip resmi API'ye yatırım yapın. Üretim ortamında ölçeklenebilen tek yol budur.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kaynaklar ve Faydalı Bağlantılar
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developers.facebook.com/docs/instagram-platform" rel="noopener noreferrer"&gt;Meta — Instagram Platform Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.facebook.com/tools/explorer/" rel="noopener noreferrer"&gt;Graph API Explorer (test aracı)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/facebook-nodejs-business-sdk" rel="noopener noreferrer"&gt;Resmi Facebook Business Node.js SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.facebook.com/docs/instagram-platform/webhooks" rel="noopener noreferrer"&gt;Instagram Webhooks Dokümanı&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.facebook.com/docs/graph-api/overview/rate-limiting" rel="noopener noreferrer"&gt;Rate Limits Detayı&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sosyobase.com/instagram-ucretsiz-takipci/" rel="noopener noreferrer"&gt;Örnek Uygulama&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sorularınızı yorumlara yazabilirsiniz. Bir sonraki yazıda Instagram'ın &lt;strong&gt;Webhooks&lt;/strong&gt; sistemi ile push tabanlı bildirimleri ele alacağım.&lt;/p&gt;

</description>
      <category>api</category>
      <category>javascript</category>
      <category>node</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
