Authentication
Every request to https://api.pff.com under /v1/ or /v2/ carries one credential in the Authorization header. There are two kinds. Both need a paid PFF Pro subscription, and the API treats them the same once it has checked them.
| Browser sign-in | API key | |
|---|---|---|
| Best for | Working at your own terminal | Scripts, CI, notebooks, or any HTTP client |
| How you get it | Your first restish pff command opens your browser |
Create one at pff.com/account/api-keys |
| What it looks like | An OAuth token Restish stores for you in ~/.config/restish/tokens.cbor |
A string starting with ak_live_, shown once |
| How long it lasts | Refreshed silently; restish pff logout ends it |
Until you revoke it on the same page |
| Where it works | Restish, which runs the browser flow for you | Restish, curl, and any language |
If you only ever use the CLI interactively, you can skip straight to Log in. Everything else on this page is about API keys.
Get an API key
- Sign in at pff.com with the account that holds your PFF Pro subscription.
- Open Account → API Keys, or go straight to https://www.pff.com/account/api-keys.
- Create a key. It is shown once, so copy it now. It starts with
ak_live_. - Store it as
PFF_API_KEYin your shell profile or your CI’s secret store. Never put it on a command line, in a repo, or in a chat message.
PFF+, free and Pro-trial accounts see an upgrade prompt on that page instead of a key manager. The CLI and API are part of paid PFF Pro only.
Revoking a key is the same page. Revocation takes effect within a minute: the API caches a successful key check for up to 60 seconds, so a request in that window may still succeed. Treat a key you no longer control as compromised and revoke it rather than waiting for it to fall out of use.
Send it
Whichever credential you hold, it travels the same way:
Authorization: Bearer <your API key or OAuth access token>
With curl or any HTTP client
whoami is the right first call. It costs nothing against your rate budget and tells you exactly what the API thinks of your credential.
export PFF_API_KEY=ak_live_... # set it once, in your shell only
curl -s https://api.pff.com/v1/auth/whoami \
-H "Authorization: Bearer $PFF_API_KEY"
{
"external_id": "93482620-...",
"email": "you@example.com",
"tier": "pro",
"entitled": true,
"entitlement_reason": null,
"credential": "api_key",
"session_expires_at": null
}
entitled: true with credential: api_key means every data operation will work. From there, any path in the API reference:
# v1 — the Premium Stats reports: snake_case query parameters
curl -s 'https://api.pff.com/v1/facet/passing/summary?league=nfl&season=2025&franchise_id=26' \
-H "Authorization: Bearer $PFF_API_KEY"
# v2 — PFF's team tables: camelCase query parameters, team by slug or franchise id
curl -s 'https://api.pff.com/v2/nfl/teams/los-angeles-rams/reports/passing?season=2025&weekGroup=REG' \
-H "Authorization: Bearer $PFF_API_KEY"
# CSV instead of JSON: v1 takes export=true, v2 takes format=csv
curl -s 'https://api.pff.com/v1/facet/passing/summary?league=nfl&season=2025&export=true' \
-H "Authorization: Bearer $PFF_API_KEY" -o passing-2025.csv
curl -s 'https://api.pff.com/v2/nfl/teams?season=2025&format=csv' \
-H "Authorization: Bearer $PFF_API_KEY" -o teams-2025.csv
Three things worth knowing when you leave Restish behind:
- Restish renames some parameters. The flag is not always the wire name:
--franchiseisfranchise_idand--gameisgame_idon/v1, and/v2uses camelCase such asweekGroupandweekTo. The OpenAPI document is the source of truth for the real names, and it is public — no credential needed. - Budgets are per account, not per key. Every account gets 100 reads and 20 exports per minute across every credential it holds. Each counted response carries
x-ratelimit-limit,x-ratelimit-remainingandx-ratelimit-reset; going over answers429with aRetry-After.whoamiandlogoutare never counted. - Every error is one JSON shape.
{"error":{"code","message","request_id","details"}}. Branch onerror.codeanderror.details.reason, never on the message text, and quoterequest_idwhen you write to support.
With Restish
Give Restish a second profile that reads the key from the environment, and pick it per command with -p ci. Your default profile keeps using browser sign-in, so both work side by side.
restish api set pff 'profiles.ci.credentials.pffApiKey.auth.type: bearer'
restish api set pff 'profiles.ci.credentials.pffApiKey.auth.params.token: env:PFF_API_KEY'
restish pff whoami -p ci # credential: api_key
restish pff passing --league nfl --season 2025 -p ci -o json
Restish reads PFF_API_KEY when it sends the request, so the key never lands in restish.json or your shell history. The full recipe, including CSV in CI, is in Log in and Exporting.
When it fails
Every refusal names its reason in error.details.reason. Restish prints the message; with curl you will see the JSON.
| Status and code | details.reason |
What it means | What to do |
|---|---|---|---|
401 unauthorized |
missing |
No Authorization header |
Send Authorization: Bearer <key> |
401 unauthorized |
malformed |
The header is not Bearer <credential> |
Check the header format and that the key was not truncated |
401 unauthorized |
invalid_signature |
The credential could not be verified: a typo, a key from another environment, or one that was deleted | Copy the key again, or create a new one |
401 unauthorized |
revoked |
The key or sign-in was revoked, by you or by support | Create a new key, or sign in again |
401 unauthorized |
expired |
An OAuth token that could not be refreshed | restish api auth logout pff, then run any command to sign in again |
401 unauthorized |
wrong_token_type |
You sent a pff.com session token (a browser cookie) instead of an OAuth access token or API key | Use an API key, or sign in through Restish |
403 forbidden |
subscription_required |
The credential is valid but the account is PFF+ or free | Upgrade to PFF Pro |
403 forbidden |
trial |
The account is on a Pro trial; the CLI and API unlock when it converts | Nothing to fix now |
403 forbidden |
unlinked_account |
A Pro account not yet linked to a subscriber record | Email contact@pff.com and quote the request_id |
429 rate_limited |
verify_ip |
Too many unknown keys tried from one address in a minute | Fix the key rather than retrying; wait for Retry-After |
503 upstream_error |
— | The identity provider did not answer | Retry after Retry-After; nothing is wrong with your key |
Next: Log in with your PFF account — the browser flow for interactive use, and the no-browser variant for SSH sessions.