Skip to content
Enjoy 20% off for 12 months on all plans. Use promo code: HYPERGROWTHClaim it

Use the public endpoints

What you can call programmatically today. The keyed /api/v1 REST API (projects, articles, generation, publishing, audits), the MCP server, keyed RSS, and the free-tool endpoints.

Open in the appUpdated 2026-10-14

There are four programmatic surfaces:

  • The /api/v1 REST API, authorized by a content API key. Projects, articles, article generation, publishing and the latest audit for whatever the key can see.
  • The MCP server at /api/mcp, which exposes the same operations to an AI assistant, plus three public lookups. See the guide.
  • Keyed RSS feeds at /api/v1/feed.xml, which syndicate your generated articles to anything that consumes a feed.
  • The free-tool endpoints (/api/free/*) that back the public free tools. No key, no workspace data, cross-origin allowed.

Get a key

Open a project, go to Publishing, and create a content API key under the pull API section. The plaintext sbk_... shows once and is stored only as a hash. Send it on every request as an X-API-Key header or as Authorization: Bearer sbk_....

A key is minted for one project and sees exactly that project's data. Revoke it from the same page; a revoked key gets 401 on the next call, indistinguishable from a key that never existed.

The endpoints

All under https://searchblueprint.io/api/v1. JSON in, JSON out. Ids are uuids. An id outside the key's scope is a 404, the same as a missing one.

Method and pathDoes
GET /projectsThe projects the key can see, each with latest_grade from its last finished audit
GET /projects/{id}/auditThe latest finished audit as { audit }: score, grade, verdict, totals, categories, recommendations, audit_running. audit: null when none has finished
GET /projects/{id}/rankingsRetired on 2026-09-17. Answers 410 Gone; the keywords you track and their history are on each website's Rankings page
GET /projects/{id}/integrationsPublishing connections: id, provider, status, auto_publish_status (the draft-vs-live default)
GET /articlesArticle summaries, newest first. ?limit= (1 to 50, default 20) and ?offset=
GET /articles/{id}One article with content_markdown and content_html
POST /articlesStart writing one. Body { "keyword": "...", "title"?: "...", "brief"?: "...", "projectId"?: "..." }. Answers 202 with the job
GET /jobs/{id}Where a generation stands: status, queue_ahead, article_id once done, error if failed
POST /articles/{id}/publishPublish through one connection. Body { "integrationId": "...", "status"?: "draft" | "publish" }
GET /feed.xmlRSS of the key's articles. A feed reader that cannot send a header uses the feed URL on Publishing > Developer: its ?token=sbf_... reads this feed and nothing else, and stops working when the key is revoked. Never put the sbk_ key itself in a URL

projectId on POST /articles is optional for a project key (it has exactly one project).

Article fields (id, title, slug, meta_description, excerpt, seedKeyword, keywords, languageCode, hero_image_url, orgWebsite, created_at, plus content_markdown and content_html on the single-article call) follow the BabyLoveGrowth contract, so a blog package built for that API consumes ours unchanged. hero_image_url is null: we do not store one, and an empty field beats a made-up one.

Write and publish an article

Start it:

curl -X POST https://searchblueprint.io/api/v1/articles \
  -H "X-API-Key: sbk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"keyword":"bridal gown alterations singapore","brief":"Brides 6 to 8 weeks out. Mention turnaround times."}'
{ "job_id": "8f1c...", "status": "queued", "keyword": "bridal gown alterations singapore", "article_id": null, "queue_ahead": null, ... }

Generation is long-running. The article-jobs cron drafts, scores and revises across several two-minute sweeps, so poll:

curl https://searchblueprint.io/api/v1/jobs/8f1c... -H "X-API-Key: sbk_YOUR_KEY"

When status is done, article_id is set. Fetch it from GET /articles/{article_id}, or publish it:

curl -X POST https://searchblueprint.io/api/v1/articles/ARTICLE_ID/publish \
  -H "X-API-Key: sbk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"integrationId":"INTEGRATION_ID"}'
{
  "sent": true,
  "requested": "draft",
  "external_url": "https://example.com/?p=145",
  "error": null,
  "event": { "status": "success", "publish_status": "draft", "url": "...", "verify_status": "skipped", "verify_http_status": null, "created_at": "..." },
  "state": "unverified"
}

Read state, not sent. sent says the CMS accepted the write. state is what the Publishing tab shows: published only after the product fetched the public URL and found the article on it; checking while the verifier waits for the host to deploy; not_live when the URL answered 404 past the grace window; unverified for a draft or a destination with no public URL; failed when the write itself failed (then error says why). Leaving status out uses the connection's own draft-vs-live setting.

Status codes

  • 401 no key, or a revoked one.
  • 400 a missing keyword, a malformed id, a status that is not draft or publish, or a keyword the writer refuses (a test string, a bare brand name).
  • 402 the workspace is out of article credits.
  • 404 the project, article, job or connection is not in the key's scope.
  • 409 the project already has an article for that keyword (the body carries existing_article_id), or the keyword is already being written.
  • 422 the publish gate blocked the article: unfilled placeholders, refusal text, more than one H1. The body lists issues.
  • 429 rate limited, with a Retry-After header.

Limits and usage

  • Reads: 120 requests per 10 minutes per IP address across /api/v1.
  • POST /articles and POST /articles/{id}/publish: a further 20 per 10 minutes per key. Each one spends credits or writes to a customer's CMS.
  • Free-tool endpoints (/api/free/*) have their own, tighter, anonymous buckets. Sustained extraction gets throttled.
  • Responses are the JSON the product's own pages consume. Fields are stable in practice but not versioned: pin your integration to the fields you use and tolerate additions.

Call a free endpoint

To call a free-tool endpoint from your own code:

  1. Open a free tool in the browser with dev tools on, run it, and read the request. The endpoint shape is exactly what you saw.
  2. Call the same endpoint from your code with the same JSON body.
  3. Handle 429 by slowing down. A minute later, the bucket is full again.

No authentication means no access to any workspace's data. The free endpoints only compute over what you send them.

If something isn't working

Everything answers 401. The header name is X-API-Key (or Authorization: Bearer), and the key starts with sbk_. A key created for one project does not open another; make a key on the project you are reading.

POST /articles answers 409 with existing_article_id. The project already has an article for that keyword. Refresh that one in the product instead of writing a competing page.

POST /articles/{id}/publish answers 404 for a connection you can see in the product. The connection belongs to a different project than the article. GET /projects/{id}/integrations lists the ones that match.

An endpoint's response shape changed. Unversioned endpoints evolve with their tools. The current shape is always observable by calling it.

Was this page helpful?

Still stuck?

Email us and the team replies from the same address. If you're signed in, Ask anything in the app answers from these guides.