Skip to main content
An API key is issued in the site’s CMS under API Keys and sent in the Authorization header, either with ApiKey in front or bare, exactly as the CMS showed it:

In the SDKs

The SDKs take the key exactly as the site showed it, from the environment, and add the header (and each signature) themselves:
.env
In Laravel, confirm the site answers, the key is accepted and the clocks agree (a signature allows five minutes):
Terminal

What a key may do

Each key holds only the scopes its app needs. A family’s …:all scope holds every scope in it, and admin:all holds them all.

Media

images:read · videos:read · photo-feeds:readFamily: media:all

Blog

content:blog:read · :write (drafts, imports too) · :publish (going live) · :delete · :generate-image (AI images, which cost money). An import’s new images and videos need the media content: scopes.Family: content:blog:all

Forms

forms:read: the submissions visitors send.Family: forms:all

Ads & conversions

conversions:read · conversions:write · attribution:read · ads-metrics:readFamily: ads:all

Request log

request-log:read · request-log:params:read

Content

content:<type>:read · :write · :publish · :delete per content family: pages covers every page type, images / files / videos their tags and folders too. Images and files go live on every save, so their :write publishes too and they have no :publish. The page templates are not pages of the site: the Pages list leaves them out, and content:pages:read-templates reads them through Page Templates. Required scopes lists each operation’s.Per family: content:<type>:all. Family: content:all

Site settings & design

global:read · global:write · global:publish · global:css (Custom CSS)Family: global:all

Languages

languages:read · languages:write (add, rename and remove languages: a save is live at once)Family: languages:all

Everything

admin:all
Every route names the scope it needs in the API reference. A key without it gets 403, and the message names the scope. One exception: on a public type, a key without the read scope still reads the published documents’ public fields, as anyone can. A key never manages people, roles, API keys or the site’s connections: those stay with a person signed in to the CMS.

What a key reaches

Every content type

List, read, create, change, publish and delete through each type’s own routes, with that type’s scopes. Required scopes lists every operation’s.

The feature routes

Media, the blog import, form submissions, conversions and attribution, ads metrics and the request log: each with its scope above.
A write scope works on drafts. On a type with drafts, a write in published mode (the default) also takes the document live, so it needs the type’s publish scope too; send aposMode=draft to change only the draft. A type without drafts (redirects, photo feeds, the media folders) needs only its write scope. A key with only a read scope sees published documents only.
Without a key, the public still reads the published documents of the public types (blog posts, authors, team members, …) as visitors see them.

Each key’s security policy

Chosen when the key is created, and shown on its Security tab.

Signing mode

Token only, signed writes, or every request signed. A signed request carries X-Fetchski-Timestamp and X-Fetchski-Signature, made with the key’s signing secret (fss_…).An unsigned request that had to be signed gets 401, and the message says so.Each signature works once, so a replayed request is refused. The SDKs sign for you: give them the secret along with the key. Signing requests shows how to sign by hand, uploads included.
Optional: the addresses the key may be used from. A call from anywhere else gets 403.
Reads and writes per minute, set per key: 600 reads and 120 writes by default. Over the limit the site answers 429 with Retry-After, the seconds to wait. The SDKs wait and retry for you.

How a signed request is checked

An upload signs the SHA-256 of its body in the body’s place, declared in X-Fetchski-Content-SHA256. Signing requests has the details and code in cURL, Node, Laravel and Python.

Trying calls in the playground

Set your site

In any endpoint’s playground, set host to your site’s host name (acme.example).

Paste your key

In Authorization, paste the key exactly as the CMS showed it (fsk_…). Nothing goes in front.

Send

The playground can’t sign, so use a key whose signing mode is token only, holding the scope the endpoint names under Access. A Conversion Data API write must always be signed, so try it from code instead (Signing requests).
The playground sends each request through the docs site’s own proxy, so your key travels with it. Make a key just for trying calls: only the scopes you are trying and an Expires date soon, then switch it off or delete it under API Keys when you are done. Never paste the key your app runs on.

People, not apps

A person signed in to the CMS uses a bearer token or a session instead: see Signing in (people) in the API reference. Apps always use an API key.