REST API REST API

REST API

Read your Prefera data and run every MCP tool over plain REST with an API key. Endpoints, authentication, errors and limits.

View as Markdown

The REST API gives scripts and automations the same data as the app. Reading uses simple GET endpoints. Everything an MCP agent can do is also a POST to /api/v1/tools/{name}. The API is included with every paid plan.

Authentication#

Create an API key in the app:

  1. Open API & MCP.
  2. Under API keys, for scripts and automations, click Create key. Only an owner or editor can.
  3. Name it after the tool that will use it. Optionally limit its areas under Advanced: limit to areas. Click Create key.
  4. Copy the key. It is shown once. Prefera keeps only a one-way fingerprint, so a lost key cannot be shown again. Revoke it and make another.

Send the key on every request as a Bearer token:

export PREFERA_KEY=your_key_here
curl -s -H "Authorization: Bearer $PREFERA_KEY" https://prefera.si/api/v1/brands
  • A key reaches one workspace and acts as the person who made it. If that person leaves the workspace, the key stops working.
  • A workspace can have 10 active keys. Revoking takes effect on the next request.
  • If the workspace is on the Free plan, requests answer 403 plan_required.
  • The tools endpoints also accept an OAuth access token from Sign in with Prefera.

Base URL#

https://prefera.si/api/v1

Responses are JSON with Cache-Control: no-store. Field names are snake_case and timestamps are ISO 8601 UTC.

Read endpoints#

These accept GET only. Any other method answers 405 method_not_allowed.

EndpointReturnsQuery
/brandsBrands in the workspacenone
/questionsTracked questions with the latest outcomebrand, status (active, suggested, archived), limit, cursor
/runsRuns, newest firstbrand, limit, cursor
/answersEvery answer of a runbrand, run (latest or a run id), question, engine, limit, cursor
/sourcesDomains the engines citebrand, run, limit, cursor
/competitorsTracked competitors and your own sharebrand
/fixesFixes, newest firstbrand, status (draft, ready, published, rechecking, done, abandoned, or live), kind, group, search, limit, cursor
/proofPublished fixes with the Proof verdictbrand
/resultsFixes with their verdict and the traffic they brought afterwardsbrand, limit
/factsApproved facts with source and verification statebrand
/movesAll moves, rankedbrand, type, limit
/opportunitiesSearch opportunitiesbrand, state, limit
/rankingsYour searches, tracked keywords or the keyword gapbrand, view (searches, tracked, gap), limit, only_winnable (true, gap only)
/authorityDomain Rating, links and the link gapbrand, limit
/pitch-listThird-party pages to pitch, rankedbrand, limit

brand is a brand id or a domain. You can leave it out when the workspace tracks one brand. With several brands the API answers 400 brand_required.

List endpoints with paging return { "data": [...], "next_cursor": "..." | null }. Pass next_cursor back as cursor to get the next page. Cursors are opaque.

export PREFERA=https://prefera.si

# Which brands does this key see?
curl -s -H "Authorization: Bearer $PREFERA_KEY" $PREFERA/api/v1/brands

# Questions, 100 at a time
curl -s -H "Authorization: Bearer $PREFERA_KEY" "$PREFERA/api/v1/questions?brand=example.com&limit=100"

# Answers of the latest run for one engine
curl -s -H "Authorization: Bearer $PREFERA_KEY" "$PREFERA/api/v1/answers?brand=example.com&run=latest&engine=perplexity"

# What the published fixes did
curl -s -H "Authorization: Bearer $PREFERA_KEY" "$PREFERA/api/v1/proof?brand=example.com"

Every MCP tool over REST#

EndpointWhat it does
GET /api/v1/toolsThe catalog: { "data": [{ name, title, description, input_schema, class, costly, annotations }] }. class is read, write or publish.
POST /api/v1/tools/{name}Run one tool. The JSON body is the tool's arguments. Send {} or an empty body for none.

A call goes through the same code as MCP. The roles, areas, access levels, rate limits, costly-action limit and approvals are the same, and a call spends exactly what it would spend over MCP. Tool names and arguments are in the MCP tool reference.

The body must be a JSON object, sent with Content-Type: application/json, up to 256 KB. Success answers 200 with { "data": { ... } }. If the key has Ask me in Prefera first and the tool is a publish tool, the answer is 202 with status: "pending_approval" and an approval_id. Poll it with the get_approval_status tool.

# What can I call?
curl -s https://prefera.si/api/v1/tools -H "Authorization: Bearer $PREFERA_KEY"

# Read: the questions you are losing
curl -s -X POST https://prefera.si/api/v1/tools/losing_questions \
  -H "Authorization: Bearer $PREFERA_KEY" -H "Content-Type: application/json" \
  -d '{"limit": 5}'

# Write: rename a fix
curl -s -X POST https://prefera.si/api/v1/tools/update_fix_details \
  -H "Authorization: Bearer $PREFERA_KEY" -H "Content-Type: application/json" \
  -d '{"fix_number": 2, "title": "Pricing page rewrite"}'

Errors#

Errors always have this shape:

{ "error": { "code": "not_found", "message": "No fix number 99 on this brand. List them with list_fixes." } }

On the tools endpoints the error object can carry extra fields, for example allowed_areas.

StatusCodes
400bad_request, invalid, invalid_cursor, brand_required, brand_ambiguous and other input errors
401invalid_api_key, invalid_token
403plan_required, forbidden, publish_required, area_not_allowed
404not_found, no_brand
405method_not_allowed
409state
413payload_too_large
415unsupported_media_type
429rate_limited, limit (with a Retry-After header)
500internal_error

What each code means and what to do is in Permissions, approvals and limits.

Limits#

  • 60 requests a minute per key. REST and MCP share the count.
  • 10 changes a minute per key on write and publish tools.
  • 20 costly tool calls an hour per key.
  • 30 requests a minute from one address with a missing or invalid key.

Over a limit you get 429 and a Retry-After header in seconds.