GET
/api/v1/me
The account the key belongs to, and the key’s own name and prefix. Useful for one thing above all: telling you that the key in this environment is the key you think it is.
Example
curl https://seonis.ai/api/v1/me \
-H "Authorization: Bearer $SEONIS_API_KEY"
GET
/api/v1/sites
Every site on the account. The id is what the article list filters on, and the domain is there so a build script can match on something it already knows rather than carry an id.
Example
curl https://seonis.ai/api/v1/sites \
-H "Authorization: Bearer $SEONIS_API_KEY"
{
"sites": [
{
"id": 5,
"name": "Peppervale",
"domain": "peppervale.co.uk",
"url": "https://peppervale.co.uk",
"language": "en",
"country": "GB",
"market": "en-GB",
"status": "active",
"articles_published": 34,
"created_at": "2026-06-02T11:04:19+00:00"
}
]
}
GET
/api/v1/articles
A page of articles, newest change first, without the writing. Ordered by when each article last changed rather than when it was written, which is what makes updated_since worth having: an article that is already live can be edited later, when a link is inserted into it or taken out again.
Query parameters
| Name |
What it does |
| site_id |
One site, from /sites. An id that is not on your account answers 404. |
| status |
One of draft, qa, needs_repair, review, ready, published, failed. Anything else is a 422 rather than an empty page. "ready" is what a build wants: written, checked, and not yet anywhere. |
| updated_since |
ISO 8601, such as 2026-09-06T00:00:00Z. Remember the timestamp of your last run and pass it back on the next one. |
| per_page |
Up to 100. The default is 25. |
| page |
From 1. meta.has_more says whether to ask for another. |
Example
curl -G https://seonis.ai/api/v1/articles \
-H "Authorization: Bearer $SEONIS_API_KEY" \
-d site_id=5 \
-d status=ready \
-d updated_since=2026-09-01T00:00:00Z \
-d per_page=50
{
"articles": [
{
"id": 918,
"site_id": 5,
"language": "en",
"status": "ready",
"title": "How to choose a pepper grinder",
"slug": "how-to-choose-a-pepper-grinder",
"meta_title": "How to choose a pepper grinder | Peppervale",
"meta_description": "Burr or blade, ceramic or steel: what matters in a grinder.",
"meta_keywords": ["pepper grinder", "burr grinder"],
"tags": ["Kitchen"],
"excerpt": "A burr grinder gives an even grind; a blade one does not.",
"key_takeaways": ["A burr grinder gives an even grind; a blade one does not."],
"faq": [{ "question": "Ceramic or steel?", "answer": "Steel for pepper, ceramic for salt." }],
"hero_image": { "url": "https://seonis.ai/storage/images/5/918.jpg", "alt": "A pepper grinder" },
"infographic_url": null,
"word_count": 1812,
"published_url": null,
"published_at": null,
"updated_at": "2026-09-06T06:31:12+00:00",
"created_at": "2026-09-05T02:14:55+00:00",
"quality": { "seo_score": 88, "language_score": 96, "language_findings": [] }
}
],
"meta": { "page": 1, "per_page": 50, "total": 1, "last_page": 1, "has_more": false }
}
GET
/api/v1/articles/{id}
One article, with everything the list leaves out: content_markdown, content_html, the FAQ as schema.org JSON-LD ready to drop into the page, and any exchange links the article carries.
The field names are the ones our webhook sends, on purpose. If you already have a webhook receiver, the same parser reads this.
Example
curl https://seonis.ai/api/v1/articles/918 \
-H "Authorization: Bearer $SEONIS_API_KEY"
{
"article": {
"id": 918,
"site_id": 5,
"language": "en",
"status": "ready",
"title": "How to choose a pepper grinder",
"slug": "how-to-choose-a-pepper-grinder",
"meta_description": "Burr or blade, ceramic or steel: what matters in a grinder.",
"content_markdown": "Grinders differ.\n\n## Burr or blade\n\nA **burr** grinder ...",
"content_html": "<p>Grinders differ.</p>\n<h2>Burr or blade</h2> ...",
"faq_schema": { "@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [] },
"hero_image": { "url": "https://seonis.ai/storage/images/5/918.jpg", "alt": "A pepper grinder" },
"word_count": 1812,
"published_url": null,
"updated_at": "2026-09-06T06:31:12+00:00",
"quality": { "seo_score": 88, "language_score": 96, "language_findings": [] },
"exchange_links": []
}
}
POST
/api/v1/articles/{id}/published
You put the article live; this is where you tell us the address. It is the one endpoint that changes anything.
It does exactly what a delivery we made ourselves does: the article is marked published, your monthly allowance counts it, the plan item closes, any exchange links in it get the address the verifier has been waiting for, and your Facebook Page is told if you have connected one. Without this call the article stays ready for ever and none of that happens.
Example
curl -X POST https://seonis.ai/api/v1/articles/918/published \
-H "Authorization: Bearer $SEONIS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://peppervale.co.uk/blog/how-to-choose-a-pepper-grinder"}'
Answers
| 200 |
Recorded. The article comes back with its new status, published_at and published_url. |
| 422 |
The url is missing or is not a full http:// or https:// address. |
| 409 |
The article is not ready to be published, or is already recorded as published. The article comes back with the refusal, so you can see which. |