Skip to main content

HTTP API

Use these endpoints from any backend. Authenticate with a Site API key. Prefer calling from your server — never expose the key in the browser.

Prefer the Node SDK?

For Next.js and Node apps, install the official client from npm:

npm install @pengraph/node

See npmjs.com/package/@pengraph/node and the Node.js guide.

Base URL​

https://seocondo.com
# self-hosted/local override: http://localhost:3000

Authentication​

Create a key in Account settings → Connections → Site API. Choose the site, read Site ID under the dropdown, name the key, click Create key, and copy the full secret from Copy your key now. It is shown once and starts with pg_live_. Send it on every request:

Authorization: Bearer pg_live_xxxxxxxx
info

The key must belong to the same site as {siteId} in the path. Keys are hashed at rest; the full secret is shown only once when created.

Errors​

{ "error": "Human-readable message" }
StatusMeaning
401Missing or invalid API key
404Post not found / not published
500Server error

List published posts​

GET /api/v1/sites/{siteId}/posts?limit=50&offset=0
Authorization: Bearer pg_live_…
Accept: application/json
ParamTypeDefaultDescription
limitinteger50Page size (1–100)
offsetinteger0Items to skip
sincedate-time—Only posts with updated_at ≥ since
curl -sS \
-H "Authorization: Bearer $PENGRAPH_API_KEY" \
-H "Accept: application/json" \
"https://seocondo.com/api/v1/sites/$PENGRAPH_SITE_ID/posts?limit=20"

200 response

{
"posts": [
{
"id": "…",
"slug": "how-to-seo",
"title": "…",
"content_html": "<p>…</p>"
}
],
"pagination": { "limit": 20, "offset": 0, "total": 1 }
}

Get a post by slug​

GET /api/v1/sites/{siteId}/posts/by-slug/{slug}
Authorization: Bearer pg_live_…
Accept: application/json
curl -sS \
-H "Authorization: Bearer $PENGRAPH_API_KEY" \
"https://seocondo.com/api/v1/sites/$PENGRAPH_SITE_ID/posts/by-slug/how-to-seo"

Each successful call counts as one read of that post — the dashboard reports the total, the last read time and a daily trend. Nothing to opt into: sites that already fetch posts this way are counted automatically, and the response is unchanged. The listing endpoint does not count reads.

When an article has moved​

When Clara merges duplicate articles or retires one, the old article is no longer published and its address should forward to the article that replaced it. A by-slug request for the old slug then answers 404 with where it moved:

{ "error": "Post moved", "redirect": { "to": "/news/the-article-that-replaced-it", "statusCode": 301 } }

The same path is also in the x-pengraph-redirect response header. Send the visitor there with a permanent redirect instead of showing "page not found". In Next.js:

const res = await fetch(`${API}/api/v1/sites/${SITE_ID}/posts/by-slug/${slug}`, { headers: { Authorization: `Bearer ${KEY}` } });
if (res.status === 404) {
const body = await res.json();
if (body.redirect?.to) permanentRedirect(body.redirect.to); // from "next/navigation"
notFound();
}

List redirects​

GET /api/v1/sites/{siteId}/redirects
Authorization: Bearer pg_live_…

Every forward your site should apply (old path → new path), newest first:

{ "redirects": [{ "from": "/news/old-article", "to": "/news/new-article", "statusCode": 301 }] }

Use it to configure redirects at build time (for example Next.js redirects() in next.config.js) or in middleware. The list is also shown under Website health → Redirects.

IndexNow key​

GET /api/v1/sites/{siteId}/indexnow-key

The site's IndexNow key as plain text, with no API key needed (the key is public by design). Search engines check that your website serves it at https://your-site/{key}.txt before they accept Clara's change notices. Point a rewrite from /{key}.txt at this URL; @pengraph/node 0.3.0's Next.js add-on does it with rewrites(). Answers 404 until SEOcondo has made a key for the site.

The Post object​

Use content_html for display. Use id as the stable key when upserting into your DB.

FieldTypeNotes
iduuidStable PenGraph ID
site_iduuidOwning site
titlestring
slugstringURL slug
contentstring | nullMarkdown
content_htmlstring | nullPrefer for blog body
excerptstring | nullListing summary
meta_* / og_*string | nullSEO / social
tags / categoryarray / stringOrganisation
canonical_urlstring | nullOriginal article URL — use as <link rel="canonical"> when syndicating
published_urlstring | nullPublish location on the owning site
published_atdate-timeISO 8601
updated_atdate-timeISO 8601
created_atdate-timeISO 8601
schema_jsonobject | nullReady-made JSON-LD; inject in a <script type="application/ld+json">
authorobject | nullByline: name, job_title, bio, credentials, image_url, profile_url, same_as[]. The post's author or the site's default author

Canonical URLs and syndication​

Each post exposes canonical_url: the absolute URL of the original article on the site that owns it. If you republish a post on another domain (for example a /news page that mirrors posts from elsewhere), emit that URL in a <link rel="canonical"> tag and treat your copy as an alternate. Never leave the same article self-canonical on two domains — search engines will pick one and drop the other.

llms.txt​

GET /api/v1/sites/{siteId}/llms.txt
Authorization: Bearer <site API key>

Returns text/plain: a ready-made llms.txt for the site (summary, key pages, published articles) built by SEOcondo. Serve it at /llms.txt on your own domain so AI assistants can understand the site. Cache it for an hour or so.

Organization JSON-LD​

GET /api/v1/sites/{siteId}/organization
Authorization: Bearer <site API key>

Returns application/ld+json: an Organization built from the brand details in SEOcondo, or a LocalBusiness with address and telephone when the site has a local business profile. Put it in a <script type="application/ld+json"> tag on the home page.

OpenAPI​

Full machine-readable spec: GET /api/v1/openapi.

Using Next.js? See the Node.js package guide.