API Overview

Base URL, authentication, request format, error handling, rate limits, and async patterns for the Boring Marketing API.

Base URL

All requests go to:

https://api.boringmarketing.com

Authentication

All authenticated requests require the X-API-Key header:

X-API-Key: bm_your_api_key_here

You receive your API key by completing Stripe checkout via POST /auth/checkout — see the Quickstart or the Authentication guide for the full flow. Store it securely — it is your sole credential.

Request format

  • Content-Type: application/json for all POST/PATCH requests
  • Query parameters: used for filtering on GET endpoints (?brand_id=, ?run_id=, ?status=)
  • Path parameters: resource IDs in the URL path (/brands/{brand_id}/opportunities)

Response format

All responses are JSON. Successful responses return the resource directly. Some list endpoints wrap results with {brand_id, total, items}; others return a bare array. Each endpoint reference page documents its exact shape — do not assume a uniform envelope.

Example wrapped response:

{
  "brand_id": "uuid",
  "total": 42,
  "opportunities": [...]
}

Example bare array response (e.g. GET /brands):

[
  { "id": "uuid-1", "domain": "...", "name": "...", "state": "new", "created_at": "..." },
  { "id": "uuid-2", "domain": "...", "name": "...", "state": "new", "created_at": "..." }
]

Response headers

Every response includes:

HeaderDescription
X-Process-TimeRequest execution time in seconds
X-Request-IDUnique 8-character request identifier
X-Skill-LatestLatest skill version (authenticated only)
X-Skill-Update-URLSkill update URL (authenticated only)

Async endpoints (202 pattern)

Pipeline endpoints return 202 Accepted immediately. The work runs in the background.

// 202 Accepted
{
  "run_id": "uuid",
  "status": "queued"
}

Poll GET /brands/{id}/runs/{run_id} for progress. See Polling Runs for details.

Endpoints that return 202:

  • POST /brands/{id}/analyze
  • POST /brands/{id}/enrich/brand
  • POST /brands/{id}/enrich/technical
  • POST /brands/{id}/enrich/llm
  • POST /brands/{id}/discover
  • POST /brands/{id}/discover-competitors
  • POST /brands/{id}/synthesize
  • POST /brands/{id}/score
  • POST /brands/{id}/brief
  • POST /brands/{id}/execute

Error responses

StatusError CodeMeaningWhat to Do
401authentication_requiredMissing or invalid X-API-Key headerCheck your API key is set correctly
402tier_limit_exceededDaily call limit reached for your tierUpgrade via POST /auth/checkout
404Resource not foundVerify the brand_id or resource ID
409Pipeline run already in progressPoll the existing run instead (check X-Running-Run-Id header)
422Validation errorCheck request body against the docs
429Rate limitedWait and retry after Retry-After seconds

All error responses follow this shape:

{
  "error": "error_code",
  "message": "Human-readable explanation",
  "actions": [
    {
      "rel": "checkout",
      "href": "/auth/checkout",
      "method": "POST",
      "description": "Start Stripe checkout to create an account"
    }
  ]
}

The actions array provides machine-readable next steps — useful for AI agents to self-recover from errors.

Rate limits

TierDaily callsBrands
Builder5001
Pro5,0003
Agency50,00010

Exceeding the daily limit returns 402. Rate-limited requests return 429 with a Retry-After header.

Path conventions

All brand CRUD and pipeline endpoints use the plural /brands/{id}/... namespace:

  • POST /brands — create a brand
  • GET /brands/{id} — read a brand
  • POST /brands/{id}/analyze — run the full pipeline
  • GET /brands/{id}/opportunities — read scored opportunities

A legacy singular /brand/* router exists for backward compatibility with older integrations but is not recommended for new work. Always prefer the plural /brands/* endpoints.