Docs

Start in a minute.

Pick the way that suits you. All three use the same account and balance.

No code

Search and download in your browser.

With your AI

Paste one prompt into ChatGPT, Claude or Cursor. It does the rest.

With code

Make an API key, send one request.

With code.

  1. 1

    Make an API key

    Sign in, then Console > API keys. New accounts get $0.10 free.

    Make a key
  2. 2

    Send a request

    One endpoint per platform. Say what you want with op.

    one profile
    curl -X POST https://apiforagents.dev/v1/linkedin \
      -H "Authorization: Bearer $AFA_KEY" -H "Content-Type: application/json" \
      -d '{"op": "profiles", "subjects": ["satyanadella"]}'
    200 { "data": { "profiles": [ { "publicId": "satyanadella", "name": "…", "headline": "…", … } ] },
          "cost": { "usd": 0.004, "balanceUsd": 4.996 }, … }
  3. 3

    Know the price first

    Add "estimateOnly": true to see the cost without paying. Add "maxUsd" to cap a call.

    • Nothing found? Not charged.
    • Sixty calls a minute per key.
    • Every field of every call is on its platform page.

The playground. No code.

The playground runs any endpoint by hand: pick a platform and what you want from it, fill a short form named in plain words (or press Use sample input), see the free quote on the Run button, run it, then read the answer as a table or JSON and download it as CSV or JSON. The same call is written out as cURL, Python and Node while you type.

  • 1Pick a platform, then what you want from it
  • 2Fill a short form in plain words, or press Use sample input
  • 3See the free quote on the Run button, then run it
  • 4Read the answer as a table or JSON, download CSV or JSON
  • 5Copy the same call as cURL, Python or Node
  • Open the playground

All 26 platforms. One key.

Every platform is live. Open one for every request and response field, an example per operation and its prices.

What the data looks like.

Pick a platform and an operation.

usernamefullNamebiographycategoryisVerifiedisBusinessfollowersfollowingmediaCountengagementRatelocationigUserIdavatarUrlexternalUrlisPrivatelastPostedAtdetails
brewlab.coffeeBrewlab CoffeeSmall-batch roasters · Austin, TXCoffee shopfalsetrue48,2103128640.031Austin, Texas456125832https://img.instagram.com/brewlab-coffee.jpghttps://www.instagram.com/brewlab-coffee-1000false2026-10-05
  • {}
mira.movesMira | PilatesPilates for busy people · new class every MondayFitness trainertruetrue182,4005401,2100.046Mumbai, India908365941https://img.instagram.com/mira-pilates.jpghttps://www.instagram.com/mira-pilates-1001false2026-10-11
  • {}
northfold.studioNorthfold StudioBrand and product designDesign studiofalsetrue9,1202013020.022London2725119329https://img.instagram.com/northfold-studio.jpghttps://www.instagram.com/northfold-studio-1002false2026-10-03
  • {}
17 fields, scroll sideways for all. Example rows: made-up names, real field names. Live calls return real data.

Reference.

Open what you need.

Request fieldsThe words every platform understands
FieldMeaning
opThe operation. One per call. Each platform page lists its own.
subjectsThe handles, URLs or ids the call is about, batched. Up to ten per call on most ops.
limitHow many rows per subject. Every listing op has a cap; the quote uses the cap, the charge uses what came back.
queryFree text for the search ops.
postedWithin1h, 24h, 7d, 30d, 90d, 1y or any, for anything with a date.
sortrelevance, newest, oldest, top, where the source offers a choice.
estimateOnlytrue returns the price and charges nothing.
maxUsdA ceiling for this call. The engine refuses above it with budget_exceeded.
freshnessauto reads your own recent answer for free; fresh forces a capture; cached refuses to spend.
Typed fields and detailsEvery row keeps its fields; details holds the rest

Each row in data has two parts. The typed fields are the stable schema of the operation: the same names and types whichever source answered, and always present. A field the source had nothing for is null, an empty list is [], and a key is never just missing, so code written against a field keeps working.

details is always the last field and always present ({} when empty). It holds everything else the source sent for that row, under our own field names. Its keys depend on the source that answered, so read them defensively. When a fact becomes common across sources it becomes a typed field.

Every operation's request fields and response fields, with their types and an example response, are on its platform's reference (Platforms, then API reference), and in openapi.json.

one row of data · linkedin companies
{
  "universalName": "acme-corp",
  "name": "Acme",
  "industry": "Software Development",
  "employeesCount": 342,
  "website": null,
  "specialities": [],
  "details": {
    "tagline": "Payments for every shop",
    "employeesByFunction": [ { "function": "Engineering", "count": 210 } ]
  }
}

`details` is always the last field: everything else the source sent for that row, under our own field names. Its keys depend on the source that answered, so read them defensively; it is `{}` when there is nothing more.

Quote first, then pay per recordestimateOnly, maxUsd, settling and refunds
  • Quote any call first: the same request with "estimateOnly": true returns cost.wouldChargeUsd, the most it can charge, and takes nothing. Quotes are free.
  • Cap a call with "maxUsd": a call that would cost more is refused with budget_exceeded before anything is held.
  • When a call runs, the quoted amount is held, the charge settles to what came back (never above the quote), and the rest returns to your balance in the same call.
  • A subject that does not come back is named in coverage with its reason and is not charged. A call that fails on our side costs nothing.
  • The same request from your account within a day is answered free from what you already fetched.
  • Prices are per record in US dollars, listed on the pricing page. Some calls also carry a minimum, such as a page of search results, which is why the quote, not the list, is the number to trust.
  • No subscription. Top up from $5; the balance never expires. New accounts and agents start with $0.10 free.
  • List prices run from $0.000045 to $0.013 per record across the platforms. Every price
The responseSame shape on every call
200 · every operation
{
  "data": { "companies": [
    { "universalName": "acme-corp", "name": "Acme", "employeesCount": 342,
      "website": null, …, "details": { "tagline": "…" } }
  ] },
  "coverage": [
    { "platform": "linkedin", "requested": 3, "returned": 2,
      "omitted": [ { "subject": "gone-corp", "reason": "not_found" } ] }
  ],
  "provenance": [
    { "subject": "acme-corp", "measuredAt": "2026-10-13T09:41:00Z",
      "source": "capture" }
  ],
  "confidence": { "acme-corp": "measured", "globex": "measured" },
  "cost": { "usd": 0.0072, "balanceUsd": 4.9928,
            "chargedFor": ["linkedin:companies:acme-corp", "linkedin:companies:globex"] },
  "notes": []
}
data
The rows, keyed by what they are (profiles, companies, posts…). Every row carries the operation's typed fields, always present and null when the source had nothing, with details last.
coverage
What you asked for against what came back, and why anything is missing.
provenance
When each subject was measured and whether it was a fresh capture or a cached read.
confidence
Measured, derived or unknown. Never a blend.
cost
What this call charged in dollars, what is left, and which subjects you paid for.
notes
Plain sentences when something needs saying. Empty when nothing does.
ErrorsWhat each one means and what to do
example: not enough balance
HTTP 402
{ "error": {
    "code": "insufficient_funds",
    "message": "This call needs $0.12. The account holds $0.03.",
    "fix": "Top up at the link, or lower limit to 20.",
    "details": { "neededUsd": 0.12, "balanceUsd": 0.03,
                 "topUp": "https://apiforagents.dev/pay/acc_7f3k" },
    "docs": "https://apiforagents.dev/docs.md#insufficient_funds" } }
CodeStatusMeaning
invalid_request400A field is missing or malformed. details names it. Fix the request; nothing was charged.
unauthorized401No key, or a revoked one. Create an account or mint a new key.
insufficient_funds402The balance cannot cover the quote. details carries neededUsd, balanceUsd and a topUp link.
budget_exceeded402The call would cost more than maxUsd. Raise maxUsd or lower limit. Nothing was charged.
subject_rejected422The source refused one of your own values, named in details. Change it; the rest of the call ran.
rate_limited429Too many calls at once for this key. retryAfterSeconds says when.
source_unavailable503The source is down or refusing. Charged nothing. Try later; the response says when.
Limits and fair useRates, caps and what is off limits
  • Sixty calls a minute per key. The 429 says when to retry.
  • One anonymous registration an hour per address, five unclaimed at once, one $0.10 trial per address and 3 per network per day while the day's trial budget lasts; an unclaimed registration expires after a day.
  • Ten subjects per call on lookups; every listing op has a cap printed on its platform page.
  • Handles come from evidence: a search result, a pasted URL, an earlier answer. A handle typed from a name bills real money and can return a different person.
  • Public pages only. Nothing behind a login is read, stored or sold.
  • One account per person or company. Accounts made to dodge a limit are closed and the balance returned.
How an AI agent signs itself upFour requests, no person needed until payment
1

Register

Standard agent registration (the auth.md protocol): one POST returns an identity, and the token endpoint turns it into a bearer. No card, no console, no person yet, and $0.10 of free credit so the first real calls work. The full recipe is at https://apiforagents.dev/auth.md.

create · request
curl -X POST https://apiforagents.dev/agent/identity \
  -H "Content-Type: application/json" -d '{"type": "anonymous"}'
# then exchange the identity_assertion for a bearer token
curl -X POST https://apiforagents.dev/oauth2/token \
  -d "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=$ASSERTION"
200 { "registration_id": "reg_…", "identity_assertion": "<jwt>", "pre_claim_scopes": ["catalog.read"],
      "claim_token": "clm_…", "post_claim_scopes": ["catalog.read", "data.run", "account.read", "account.billing"] }
200 { "access_token": "afa_…", "token_type": "Bearer", "expires_in": 86400, "scope": "catalog.read" }
2

Quote

Any call with estimateOnly returns the exact dollar amount it would charge and takes nothing. Do this before every paid call.

quote · request
curl -X POST https://apiforagents.dev/v1/linkedin \
  -H "Authorization: Bearer $AFA_KEY" -H "Content-Type: application/json" \
  -d '{"op": "profiles", "subjects": ["satyanadella", "jeffweiner08"], "estimateOnly": true}'
200 { "cost": { "usd": 0, "wouldChargeUsd": 0.0072 },
      "coverage": [ { "requested": 2, "returned": 0 } ] }
3

Get claimed, then funded

Paid calls need a person. Ask for a six-digit code and a link, show both to them, and poll. Once they type the code the account is theirs and your next token carries data.run. The first top-up is $5; a 402 on any call carries the link for it.

topup · request
# ask for a code and a link, then show both to the person
curl -X POST https://apiforagents.dev/agent/identity/claim \
  -H "Content-Type: application/json" \
  -d '{"claim_token": "'$CLAIM'", "email": "you@example.com"}'
# poll until they have typed the code
curl -X POST https://apiforagents.dev/oauth2/token \
  -d "grant_type=urn:workos:agent-auth:grant-type:claim&claim_token=$CLAIM"
200 { "claim_attempt": { "user_code": "123456", "verification_uri": "https://apiforagents.dev/claim?claim_attempt_token=…", "expires_in": 600, "interval": 5 } }
200 { "access_token": "afa_…", "scope": "catalog.read data.run account.read account.billing", "identity_assertion": "<jwt>" }
4

Run

Same request without the flag. The charge equals the quote. A subject that does not come back is refunded and named in coverage.

run · request
curl -X POST https://apiforagents.dev/v1/linkedin \
  -H "Authorization: Bearer $AFA_KEY" -H "Content-Type: application/json" \
  -d '{"op": "profiles", "subjects": ["satyanadella", "jeffweiner08"]}'
200 { "data": [ … ], "coverage": [ … ], "provenance": [ … ], "confidence": { … },
      "cost": { "usd": 0.0072, "balanceUsd": 4.9928 }, "notes": [] }

The full recipe for agents: /auth.md. This page as text: /docs.md.