API tutorial · September 2026

Instagram Creator Data API: Bio, Contact Links, and Category

The creator-contact signals Instagram actually exposes — bio, links, category, contact method, city — and the fields it does not, so you do not build a pipeline on data that never arrives.

· 11 minute read

If you are searching for an “Instagram email finder API”, the honest answer is uncomfortable: the private Instagram data that a paid API can return does not include the creator’s email address or phone number. Those fields exist in Instagram’s own business-contact model, but a public-data extraction layer cannot legitimately hand them to you, and this API does not.

What you can get is a stable set of contact signals that are public by design: the bio, the external link, the business or brand category, the declared contact method, and location fields. For most outreach and CRM use cases, that is enough to route a creator correctly — and it is defensible, because you are reading what the creator published. This guide shows exactly which fields you get, with no invented columns.

Read this before you plan the pipeline. GramScraper deliberately filters structured contact fields out of API responses. A response will not contain the profile’s public_email or its phone fields, even when the account has set them, because those datasets are outside the product’s offer. Any guide that promises you an email-address-per-follower API from a public-data provider is describing something that does not exist here.

What the profile endpoint returns

A single call — /v1/user/by/username — is the whole creator-data toolkit. It costs 1 credit and returns the profile object. The contact-relevant fields are:

FieldWhat it tells you
biographyThe full bio text. Often contains an email or a DM instruction in plain text — see the extraction section below.
external_urlThe link in the bio. Frequently a Linktree, a media kit, or a brand site — the natural next hop for outreach.
category_nameThe account category (for example a creator or business vertical), when set.
business_category_nameThe business category, when the account is a business account.
business_contact_methodHow the account says it wants to be contacted — for example CALL or UNKNOWN.
is_business / account_typeWhether this is a business account, and Instagram’s numeric account-type code. is_business is the reliable boolean to branch on.
city_name, address_street, zipBusiness location fields, when the account has published them.
latitude / longitudeBusiness map coordinates, when set.
follower_count / following_count / media_countAudience size signals, used to prioritise. See the followers API page for tracking those over time.
is_verifiedVerification status.

Profile routes available for this: /v1/user/by/username, /v2/user/by/username, /v1/user/by/id, /v1/user/by/url, plus the richer /gql/user/web_profile_info and /gql/user/about. The v2 and GraphQL variants also expose bio_links, Instagram’s multi-link feature, which can hold more than the single external_url.

Worked example: a real business account

Fetching a well-known business account shows the shape of the data. This is a real response body, trimmed to the contact-relevant keys:

curl
curl -s -H "Authorization: Bearer gs_your_key" \
  "https://gramscraper.com/api/instagram/v1/user/by/username?username=nike"
Response — contact-relevant fields (trimmed)
{
  "pk": 13460080,
  "username": "nike",
  "full_name": "Nike",
  "biography": "Just Do It.",
  "external_url": "http://empli.fi/nike",
  "is_business": true,
  "account_type": 2,
  "business_contact_method": "CALL",
  "category_name": null,
  "business_category_name": null,
  "address_street": "One Bowerman Dr",
  "city_name": "Beaverton, Oregon",
  "zip": "97005",
  "latitude": 45.5076448,
  "longitude": -122.8269159,
  "public_phone_country_code": "",
  "follower_count": 291166543,
  "following_count": 266,
  "media_count": 1667,
  "is_verified": true
}

Read that response carefully and the honest picture is right there. You get the bio, the bio link, the declared contact method, the category slots, and a full business address with coordinates. You do not get a public_email, and the phone-number fields that would carry a business number are absent. public_phone_country_code exists as an (empty) country-code slot but arrives without its number, so treat it as unusable.

Category fields are often null even on large verified accounts, as nike shows. Never treat category_name as guaranteed — design your logic to fall back to biography and external_url.

Node.js

Node.js — build a creator contact record
const BASE = "https://gramscraper.com/api/instagram";
const KEY = process.env.GRAMSCRAPER_API_KEY;

async function creatorRecord(username) {
  const url = new URL(`${BASE}/v1/user/by/username`);
  url.searchParams.set("username", username);

  const res = await fetch(url, { headers: { Authorization: `Bearer ${KEY}` } });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const p = await res.json();

  return {
    username: p.username,
    full_name: p.full_name,
    followers: p.follower_count,
    verified: p.is_verified,
    is_business: p.is_business,
    bio: p.biography || "",
    link: p.external_url || null,
    category: p.category_name || p.business_category_name || null,
    contact_method: p.business_contact_method || null,
    city: p.city_name || null,
    // Record what you actually got, so nothing silently becomes a fact.
    has_email_field: false,
  };
}

console.log(await creatorRecord("natgeo"));

Python

Python — build a creator contact record
import os, re, requests

BASE = "https://gramscraper.com/api/instagram"
HEADERS = {"Authorization": f"Bearer {os.environ['GRAMSCRAPER_API_KEY']}"}

def creator_record(username):
    r = requests.get(
        f"{BASE}/v1/user/by/username",
        headers=HEADERS,
        params={"username": username},
    )
    r.raise_for_status()
    p = r.json()
    return {
        "username": p.get("username"),
        "full_name": p.get("full_name"),
        "followers": p.get("follower_count"),
        "verified": p.get("is_verified"),
        "is_business": p.get("is_business"),
        "bio": p.get("biography") or "",
        "link": p.get("external_url"),
        "category": p.get("category_name") or p.get("business_category_name"),
        "contact_method": p.get("business_contact_method"),
        "city": p.get("city_name"),
    }

print(creator_record("natgeo"))

Extracting a work email from the bio — the legitimate path

Creators who want inbound mail usually publish it themselves. Your job is to read what they chose to publish, not to guess a private address. Parse the bio and the bio link:

Python — find a self-published contact in the bio
import re

EMAIL_RE = re.compile(r"[\w.+-]+@[\w-]+\.[\w.-]+")
URL_RE = re.compile(r"https?://\S+|[\w-]+\.(?:com|co|io|net|link)/\S*")

def contact_signals(profile):
    bio = profile.get("bio") or ""
    signals = {
        "emails": EMAIL_RE.findall(bio),
        "links": URL_RE.findall(bio.replace("\n", " ")),
        "dm_instruction": bool(re.search(r"\bdm\b|direct message", bio, re.I)),
        "external_url": profile.get("link"),
    }
    # The bio link is a first-class signal even when the bio has no address.
    return signals

Two practical rules. First, only treat an address found in the bio as usable when it is clearly published for business contact — a “for collabs: [email protected]” is a standing invitation, whereas a personal address appearing incidentally is not. Second, always follow external_url: for creators represented by an agency, that link is usually the fastest correct route, and mail sent to an agency gets answered faster than a cold DM.

Following the link one hop

A media kit or Linktree page is a normal web page. Fetch it yourself and look for a contact route. That is ordinary web scraping of a page the creator published for exactly this purpose — not a bypass of Instagram’s contact model — and it is where most of your real coverage will come from.

What people build with this

Influencer outreach lists

Filter a candidate list by follower_count band, is_verified, and category, then pull external_url as the outreach destination. A CRM row becomes: handle, name, size, category, city, contact route.

CRM enrichment

You already have a creator in your CRM; you want to know whether they have since added a business category, changed their bio link, or moved city. Re-run the lookup and diff. At 1 credit each, refreshing 500 creators is 500 credits.

Market mapping

Pull the same fields across a niche — for example 200 accounts in a category — and aggregate on category and city to see how a market is distributed. This uses only fields that reliably exist, so the output is not full of empty columns.

Honest limitations

Get 100 free API credits

Run a real creator through the profile endpoint and confirm the field set against your requirement before you build. Starter is $9 for 10,000 non-expiring credits.

Create a free account →