Read many creators in one call
const url = 'https://atlas-dev.aspire.io/api/v1/creators?ids=instagram%3Asomehandle%2Chttps%3A%2F%2Fwww.instagram.com%2Fp%2FDEF456%2F&network=instagram&asOrg=acme-brand&asProfile=acme-brand-main';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url 'https://atlas-dev.aspire.io/api/v1/creators?ids=instagram%3Asomehandle%2Chttps%3A%2F%2Fwww.instagram.com%2Fp%2FDEF456%2F&network=instagram&asOrg=acme-brand&asProfile=acme-brand-main' \ --header 'Authorization: Bearer <token>'The batch form of the single read: every submitted identifier lands in exactly one of three buckets inside a single 200 — data (held, matching a single read’s own data field for the same creator with no ?include=posts — each entry is { channels: Channel[] }, never { channels, posts }, regardless of what a corresponding single read would return with include=posts set), fetching (discovery running — re-submit after Retry-After), or unavailable (not obtainable — check each entry’s reason). A data entry carries no echo of the identifier you submitted (unlike fetching/unavailable) — correlate it by matching any of its channels’ own network/externalId/username instead. Mixed networks are fine; identifiers on unsupported networks bucket as unavailable with not-supported-on-network. Batch entries never carry posts. Requires creators:read.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Query Parameters
Section titled “Query Parameters”Comma-separated identifiers, 1–100 per request (over-cap is a 400, never a silent truncation). Each identifier is one of three forms: qualified (instagram:somehandle), a post/profile URL, or bare (resolved against network). Whitespace around commas is tolerated; empty tokens are dropped.
Examples
instagram:somehandle,https://www.instagram.com/p/DEF456/Default network for BARE identifiers in ids (defaults to instagram). Never a filter — a qualified (instagram:handle) or URL identifier carries its own network.
Names a target Organization to act on, when it differs from your credential’s own organization — for a delegated agency/partner relationship. This is the organization’s SLUG, not its id (acme-brand, not org_m05a9wv8g7kqj11d): it is resolved by slug, so passing an id answers 404 not-found with the id echoed back — a message that reads like the organization does not exist rather than like the wrong identifier was sent. You must hold a role in the named organization, by direct membership or a delegated grant; otherwise the answer is 403 forbidden.
Attribution only — associates this read’s discovery/refresh work with one of your Profiles, resolved within your organization. Like asOrg, this is the Profile’s SLUG rather than its id.
Responses
Section titled “Responses”Always 200 — per-item outcomes are buckets, never per-item HTTP statuses.
The three-bucket batch envelope, returned with HTTP 200 always — per-item outcomes are buckets, never per-item HTTP statuses. Every submitted identifier appears in exactly one bucket. The response’s Retry-After header is present iff fetching is non-empty.
object
Documents we hold — same shape as the single read’s 200 body.
object
This creator’s channels. Always at least one. UNORDERED — carries no meaningful index; do not read position as significance. Intended to eventually return every channel a creator owns, not only the ones a search filter matched — today this is always exactly one channel; multi-channel hydration is not yet implemented.
object
The social network this channel belongs to (e.g. instagram, tiktok). An open value — more networks may be added over time.
The channel’s network-native identifier (Instagram’s numeric user id, TikTok’s open_id), stable for the account’s lifetime.
The channel’s current @handle.
Total number of followers.
Whether the network has verified this channel’s identity. On tiktok this is the platform’s own verification flag. On instagram it is sourced only from a successful Creator Marketplace lookup — false also covers accounts Creator Marketplace doesn’t attest for (e.g. a brand/business account, which it doesn’t cover, or when Creator Marketplace credentials aren’t configured for the org). On either network, false means ‘not verified as far as this API can attest,’ not a platform statement that the account is unverified.
The channel’s country.
The Instagram-specific field set for this channel (see below) — every field is null if not yet observed, except the booleans hasProfilePic/isPublished/isPaidPartnershipMessagesEnabled (which publish false when not observed — see each field’s own description), audienceDemographics, which is absent unless we hold a live audience-insights grant, and creatorEngagedAccountsBreakdowns, which is absent unless a Creator Marketplace fetch found this creator with at least one breakdown.
object
The account’s display name (distinct from username).
The account’s bio text.
The website URL listed on the account’s profile.
URL of the account’s current profile picture.
Number of accounts this creator follows.
Total number of posts on this account.
The creator’s gender, when known. Common values are male, female, and unknown (undisclosed), though other values may appear over time.
The age range the creator belongs to (e.g. 18-24), or unknown when undisclosed. Other ranges may appear over time.
Whether the creator has been onboarded to the Creator Marketplace.
URL of the creator’s Portfolio.
Contact email on file for this creator. Not necessarily verified, and no format validation is applied.
Whether the creator has enabled paid partnership messages through the Instagram Creator Marketplace. false also covers accounts Creator Marketplace doesn’t cover (e.g. brand/business pages), where Meta exposes no such setting.
Whether the creator has branded content or partnership ads collaboration experience in the past year.
The brands the creator has collaborated with on branded content or partnership ads in the past year.
The badges of the creator. Meta does not publish a closed or confirmed set of possible values — treat as opaque strings, not an enum.
Distinct accounts reached, from Meta’s account-level insights API, over a rolling ~30-day window. null if never observed.
Distinct accounts reached, from Meta’s Creator Marketplace API. Reflects month-to-date (resets at the start of each calendar month) — Meta does not honor a rolling-window request for this metric. null if never observed.
Whether the account currently has a profile picture set. false also covers accounts where Meta exposes no value (the field is limited to Page-backed Instagram accounts).
Whether the account is published. false also covers accounts where Meta exposes no value (the field is limited to Page-backed Instagram accounts).
Number of distinct accounts that engaged with this creator’s content. Meta’s current default lookback window is the calendar month to date (this_month), not guaranteed by Meta.
Reels interaction rate, as a percentage of reel views (e.g. 7.2 means 7.2%). Meta’s current default lookback window is a rolling 90 days (last_90_days), not guaranteed by Meta.
Reels hook rate — the percentage of viewers who kept watching past the opening seconds (e.g. 42 means 42%). Meta’s current default lookback window is a rolling 90 days (last_90_days), not guaranteed by Meta.
Engaged-audience demographics, present only for accounts we hold a live grant with audience-insights scope for. Its internal shape is NOT part of this API’s v1 compatibility promise and may change without notice — today it is normalized: gender labels are male/female/unknown, country is an ISO 3166-1 alpha-2 code, a city’s state segment is its 2-letter USPS abbreviation, and a result MAY carry count/percentage, both, or neither — absent when Meta supplied no value for that cell. Meta returns 5 breakdown dimensions: country, city, gender, age, and the combined age,gender. Results may not be exhaustive. Separately, for the age, gender, and combined age,gender breakdowns: a cell absent from the results may be genuinely zero or withheld by Meta — its absence alone doesn’t tell you which.
Creator Marketplace’s creator_engaged_accounts metric broken down by gender/age/top_countries/top_cities, present only when a Creator Marketplace fetch found this creator and returned at least one breakdown. Its internal shape is NOT part of the v1 compatibility promise — normalized the same way as audienceDemographics, but a result MAY carry percentage (Meta’s own value, already a share of the metric’s total) — absent when Meta supplied no value for that cell — and never a count, since Meta supplies no raw count for this metric to derive one from.
The TikTok-specific field set for this channel (see below) — every field is null if not yet observed.
object
The account’s display name (distinct from username).
The account’s bio text.
URL of the account’s current profile picture.
Whether this is a TikTok Business Account.
Number of accounts this creator follows.
Total number of videos on this account.
Total likes accumulated across this account’s videos.
The creator’s listed rate for sponsored content, when published — a currency code plus a numeric rate.
object
The audience’s gender breakdown, each a gender and its percentage share.
object
The audience’s age-range breakdown, each a range and its percentage share.
object
The audience’s country breakdown, each a country and its percentage share.
object
The audience’s device breakdown, each a device and its percentage share.
object
The audience’s app-usage breakdown, each a usage bucket and its percentage share.
object
Median view count across this creator’s recent videos.
This creator’s overall engagement rate.
TikTok’s own estimated price for a sponsored post by this creator.
The currency creatorPrice is denominated in.
ISO 8601 timestamp of when this channel’s current snapshot was produced — how fresh this document is.
This creator’s recent posts, included only when the request asked for ?include=posts. Absent (not empty) when not requested. Posts are not nested per channel — to attribute a post back to one of channels, match its author.accountId against that channel’s externalId.
object
The social network this post is on.
The post’s network-native identifier.
The post’s public permalink.
ISO 8601 timestamp of when the post was published.
The post’s caption text.
The post’s media type — video, image, or carousel.
The post’s media items, one entry per media node (multiple entries for a carousel), in order. mediaUrl/thumbnailUrl are null whenever that asset isn’t available yet (not yet archived, or the CDN isn’t configured) — the two are resolved independently, so one can be null while the other isn’t.
object
Total like count.
Total comment count.
Total view count.
Total share count.
Total save count.
The post’s author — a thin, no-PII summary of the creator account. author.accountId is the only way to attribute a post back to one of Creator.channels — match it against that channel’s externalId.
object
The post author’s network-native account identifier.
The post author’s @handle.
Total number of followers the post author has.
Whether the post author’s identity is verified on the network. false also covers accounts Creator Marketplace doesn’t attest for — same semantics as the channel-level verified.
The post author’s country.
A submitted identifier we don’t hold yet — this batch read started (or joined) discovery for it. Re-submit it after Retry-After.
object
The identifier exactly as submitted.
ISO 8601 instant — every fetching entry in one response shares the same instant, and the response’s Retry-After header (present iff this bucket is non-empty) is its delta-seconds twin.
A submitted identifier that resolved to ‘not obtainable’. Check reason (and retryable) — most values mean stop retrying.
object
The identifier exactly as submitted.
Machine-readable, open vocabulary (additions are non-breaking). outside-recent-media-window and author-unresolved occur on posts only. internal-error means the failure was on our side — retryable is true.
Present, and always true, when the failure was on our side rather than a fact about the entity — retry after a few minutes. Absent means terminal: stop retrying.
Examples
{ "data": [ { "channels": [ { "network": "instagram", "externalId": "17841400000000000", "username": "somehandle", "followersCount": 12345, "verified": false, "country": "US", "instagram": { "name": "Some Handle", "biography": "Creator bio goes here.", "website": "https://example.com", "profilePictureUrl": "https://cdn.aspire.io/media/3f9a1c2e-4b6d-4e8a-9c1f-7d2b5a6e9f10", "followsCount": 200, "mediaCount": 350, "gender": "female", "ageBucket": "25-34", "onboardedStatus": true, "portfolioUrl": "https://example.com/portfolio", "isPaidPartnershipMessagesEnabled": true, "hasBrandPartnershipExperience": true, "pastBrandPartnershipPartners": [ "brand-a", "brand-b" ], "badges": [ "top-creator" ], "reach": 8200, "creatorMarketplaceReach": 8500, "hasProfilePic": true, "isPublished": true, "creatorEngagedAccounts": 1362, "reelsInteractionRate": 7.2, "reelsHookRate": 42 }, "updatedAt": "2026-08-01T00:00:00.000Z" } ] } ], "fetching": [ { "id": "instagram:fetching-handle", "retryAfter": "2026-08-01T00:30:05.000Z" } ], "unavailable": [ { "id": "instagram:gone-handle", "reason": "account-not-discoverable" } ]}Headers
Section titled “Headers”On every response, success or failure. Quote it when contacting support — it’s the fastest way for Aspire to find exactly your request.
Present iff fetching is non-empty — delta-seconds until those entries are worth re-submitting (every fetching entry in one response shares the same instant).
A malformed request — e.g. a batch outside the 1–100 identifier cap, an unrecognized include value, url and ids supplied together, or an unparseable JSON body.
The one JSON shape every error response (any status other than 200/202) carries.
object
The one JSON shape every error response (any status other than 200/202) carries, wrapped under error in apiErrorResponseSchema.
object
The stable, machine-readable error code. Maps 1:1 to the HTTP status.
Human prose for logs and error screens. Never parse it.
Optional structured extras. Today’s one use: a read-miss 404’s reason (and retryable, when our fault) — see details.reason.
object
Machine-readable, open vocabulary — same values a batch unavailable entry’s own reason carries.
Present (and true) only when the failure was on our side rather than a fact about the entity — retry after a few minutes. Absent means terminal: stop retrying.
Example
{ "error": { "code": "invalid-input", "message": "ids must contain between 1 and 100 identifiers, got 101" }}Headers
Section titled “Headers”On every response, success or failure. Quote it when contacting support — it’s the fastest way for Aspire to find exactly your request.
Missing, malformed, expired, or revoked credential.
The one JSON shape every error response (any status other than 200/202) carries.
object
The one JSON shape every error response (any status other than 200/202) carries, wrapped under error in apiErrorResponseSchema.
object
The stable, machine-readable error code. Maps 1:1 to the HTTP status.
Human prose for logs and error screens. Never parse it.
Optional structured extras. Today’s one use: a read-miss 404’s reason (and retryable, when our fault) — see details.reason.
object
Machine-readable, open vocabulary — same values a batch unavailable entry’s own reason carries.
Present (and true) only when the failure was on our side rather than a fact about the entity — retry after a few minutes. Absent means terminal: stop retrying.
Example
{ "error": { "code": "unauthorized", "message": "invalid credential" }}Headers
Section titled “Headers”On every response, success or failure. Quote it when contacting support — it’s the fastest way for Aspire to find exactly your request.
Valid credential, but it lacks the <resource>:<action> permission this operation requires (stated in the operation description).
The one JSON shape every error response (any status other than 200/202) carries.
object
The one JSON shape every error response (any status other than 200/202) carries, wrapped under error in apiErrorResponseSchema.
object
The stable, machine-readable error code. Maps 1:1 to the HTTP status.
Human prose for logs and error screens. Never parse it.
Optional structured extras. Today’s one use: a read-miss 404’s reason (and retryable, when our fault) — see details.reason.
object
Machine-readable, open vocabulary — same values a batch unavailable entry’s own reason carries.
Present (and true) only when the failure was on our side rather than a fact about the entity — retry after a few minutes. Absent means terminal: stop retrying.
Example
{ "error": { "code": "forbidden", "message": "token lacks creators:read permission" }}Headers
Section titled “Headers”On every response, success or failure. Quote it when contacting support — it’s the fastest way for Aspire to find exactly your request.
A read-miss (branch on details.reason — retry only if details.retryable is true, never on message prose), an unknown asProfile (deliberately indistinguishable from a nonexistent one, so slugs are not enumerable), or an unsupported network. details.reason is an open vocabulary (additions are non-breaking). Values today: account-not-discoverable (creators — terminal; a personal account and a nonexistent handle are indistinguishable), not-supported-on-network (the network isn’t supported yet — terminal until it ships), outside-recent-media-window (posts — terminal), author-unresolved (posts), unparseable-identifier, and internal-error with details.retryable: true (our fault — retry after a few minutes).
The one JSON shape every error response (any status other than 200/202) carries.
object
The one JSON shape every error response (any status other than 200/202) carries, wrapped under error in apiErrorResponseSchema.
object
The stable, machine-readable error code. Maps 1:1 to the HTTP status.
Human prose for logs and error screens. Never parse it.
Optional structured extras. Today’s one use: a read-miss 404’s reason (and retryable, when our fault) — see details.reason.
object
Machine-readable, open vocabulary — same values a batch unavailable entry’s own reason carries.
Present (and true) only when the failure was on our side rather than a fact about the entity — retry after a few minutes. Absent means terminal: stop retrying.
Example
{ "error": { "code": "not-found", "message": "no creator found for instagram/somehandle", "details": { "reason": "account-not-discoverable" } }}Headers
Section titled “Headers”On every response, success or failure. Quote it when contacting support — it’s the fastest way for Aspire to find exactly your request.
Over the per-principal request budget (fixed one-minute window; default 60 requests/minute, raisable per Service Account). Rejected requests still count against the window. Honour Retry-After.
The one JSON shape every error response (any status other than 200/202) carries.
object
The one JSON shape every error response (any status other than 200/202) carries, wrapped under error in apiErrorResponseSchema.
object
The stable, machine-readable error code. Maps 1:1 to the HTTP status.
Human prose for logs and error screens. Never parse it.
Optional structured extras. Today’s one use: a read-miss 404’s reason (and retryable, when our fault) — see details.reason.
object
Machine-readable, open vocabulary — same values a batch unavailable entry’s own reason carries.
Present (and true) only when the failure was on our side rather than a fact about the entity — retry after a few minutes. Absent means terminal: stop retrying.
Example
{ "error": { "code": "rate-limited", "message": "rate limit exceeded (60 requests/minute for this principal)" }}Headers
Section titled “Headers”On every response, success or failure. Quote it when contacting support — it’s the fastest way for Aspire to find exactly your request.
Delta-seconds (RFC 9110 §10.2.3), always ≥ 1. The server controls the backoff — polling faster than this burns rate limit and gets the data no sooner.
Our fault. The message is deliberately generic — internal error detail is never exposed. Safe to retry with backoff.
The one JSON shape every error response (any status other than 200/202) carries.
object
The one JSON shape every error response (any status other than 200/202) carries, wrapped under error in apiErrorResponseSchema.
object
The stable, machine-readable error code. Maps 1:1 to the HTTP status.
Human prose for logs and error screens. Never parse it.
Optional structured extras. Today’s one use: a read-miss 404’s reason (and retryable, when our fault) — see details.reason.
object
Machine-readable, open vocabulary — same values a batch unavailable entry’s own reason carries.
Present (and true) only when the failure was on our side rather than a fact about the entity — retry after a few minutes. Absent means terminal: stop retrying.
Example
{ "error": { "code": "internal-error", "message": "the request could not be completed" }}Headers
Section titled “Headers”On every response, success or failure. Quote it when contacting support — it’s the fastest way for Aspire to find exactly your request.
A dependency needed to verify the credential is unreachable — the credential was neither accepted nor rejected. Retry with backoff.
The one JSON shape every error response (any status other than 200/202) carries.
object
The one JSON shape every error response (any status other than 200/202) carries, wrapped under error in apiErrorResponseSchema.
object
The stable, machine-readable error code. Maps 1:1 to the HTTP status.
Human prose for logs and error screens. Never parse it.
Optional structured extras. Today’s one use: a read-miss 404’s reason (and retryable, when our fault) — see details.reason.
object
Machine-readable, open vocabulary — same values a batch unavailable entry’s own reason carries.
Present (and true) only when the failure was on our side rather than a fact about the entity — retry after a few minutes. Absent means terminal: stop retrying.
Example
{ "error": { "code": "unavailable", "message": "temporarily unavailable" }}Headers
Section titled “Headers”On every response, success or failure. Quote it when contacting support — it’s the fastest way for Aspire to find exactly your request.