Skip to main content

Brand ids

{id} in a path accepts two forms:
  • the platform id — the brand’s id on the platform it advertises from. It’s numeric for brands (1234567890) and a handle for creators (saschafitness). This is what /brands/search returns as id.
  • the brand UUID — a1b2c3d4-.... Accepted if you already have one, but search never returns this form.
Pass whatever search gave you straight through — don’t try to convert between forms. An id that matches no brand returns 404 brand_not_found.

Nothing is paginated

There is no cursor, no page, and no total. Each collection endpoint returns up to its cap in one response, and count is the number of rows in that response — not how many exist. If count equals the limit you asked for, assume there is more data you didn’t get, and raise the limit. Two endpoints have no headroom to raise: /brands/{id}/landing-pages has a default and a maximum that are both 100, and /brands/search with mode: "smart" returns at most 20 however high you set count. For those, a full page means you have all the API will give you, not that you asked for too little.

Ordering

Because results are ranked, truncating with limit keeps the strongest rows — you lose the tail, not an arbitrary subset.

Modes take different fields

/brands/search validates a different shape per mode, and these rules can’t be expressed in JSON Schema, so a generated client won’t catch them:
  • mode: "ids" requires brandIds. count is ignored — you get one row per id that matches a brand. Ids that match nothing don’t raise 404; they come back in unmatchedIds, so brands can be shorter than the list you sent. You’re billed only for the rows you got.
  • mode: "brand_name" requires brandNames and type.
  • mode: "smart" requires query and type, and rejects sortBy, because results are ordered by relevance.
Violations return 400 invalid_request with the offending field in details.

Rate limits

Three limits apply at once, and the lowest wins:
  • 60 requests/minute per IP address
  • 120 requests/minute per key
  • a per-endpoint limit:
    • 60/min for /brands/search, GET /brands/{id}, and POST /brands/{id}/media
    • 50/min for POST /brands/index
    • 300/min for GET /brands/{id}/index-status
    • 30/min for /insights and /landing-pages
Exceeding any of them returns 429 rate_limited with a Retry-After header. Wait that many seconds rather than retrying immediately. Note the per-IP limit is lower than the per-key limit and is checked first, so a single server making concurrent calls will hit 60/min before it hits 120/min. For /brands/search, GET /brands/{id}, and POST /brands/{id}/media the per-endpoint limit is also 60/min, so the per-IP limit reaches it first and the per-endpoint one never fires on its own. GET /brands/{id}/index-status is 300/min, so IP and key limits win first there too. POST /brands/index (50/min) and the 30/min endpoints can report a per-endpoint 429.

Requests and responses

  • Server-to-server only. There’s no CORS, so browser calls will fail. Keep the key on your backend.
  • POST bodies are capped at 100 KB. Larger returns 413 payload_too_large.
  • Responses are never cached — every response carries Cache-Control: no-store.
  • HEAD is not supported on /insights, /landing-pages, and /index-status. It returns 405 method_not_allowed. On the metered GET endpoints that’s because it would run the query and bill for it without returning a body.
  • Any other wrong method — a GET on a POST path, say — also returns 405 method_not_allowed, with an Allow header listing what the path accepts.

Fields can be added

Response objects are open. New fields may appear without a version bump, so parse defensively and ignore what you don’t recognize. Fields will not be removed or change meaning without a new API version.