Read many creators in one call
const url = 'https://atlas.aspire.io/api/v1/creators?ids=instagram%3Asomehandle%2Chttps%3A%2F%2Fwww.instagram.com%2Fp%2FDEF456%2F&network=instagram';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.aspire.io/api/v1/creators?ids=instagram%3Asomehandle%2Chttps%3A%2F%2Fwww.instagram.com%2Fp%2FDEF456%2F&network=instagram' \ --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.
Attribution only — associates this read’s discovery/refresh work with one of your Profiles, resolved within your organization.
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.
The channel’s network-native identifier (Instagram’s numeric user 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. Sourced only from a successful Instagram Creator Marketplace lookup — null for a brand/business account (Creator Marketplace doesn’t cover them) or when Creator Marketplace credentials aren’t configured for the org.
The channel’s country.
The Instagram-specific field set for this channel (see below) — every field is null if not yet observed, except audienceDemographics, which is absent unless we hold a live audience-insights grant.
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.
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.
This account’s reach metric (distinct accounts that saw its content).
Whether the account currently has a profile picture set.
Whether the account is published.
Number of distinct accounts that engaged with this creator’s content.
Reels interaction rate, as a percentage of reel views (e.g. 7.2 means 7.2%).
Reels hook rate — the percentage of viewers who kept watching past the opening seconds (e.g. 42 means 42%).
Engaged-audience demographics, present only for accounts we hold a live grant with audience-insights scope for. Its internal shape is NOT part of the v1 compatibility promise (D19) — Meta’s own payload, passed through opaquely. Meta returns 5 breakdown dimensions: country, city, gender, age, and the combined age,gender. Results may not be exhaustive.
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.
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.
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://example.com/pic.jpg", "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": 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.