Launch offer: 3-day trial for $1, then $39/mo. Cancel anytime.

For developers

Publish to an existing API with Custom API

Custom API sends each article to an HTTP endpoint you already have, such as a headless CMS or your own backend, with the method, headers and JSON body you define. Use it when you can't or don't want to write a new receiver: you adapt the request to the API instead.

Last reviewed October 1, 2026.

Before you start

  • An API endpoint that creates a blog post from a JSON request.
  • The way that API authenticates (for example a bearer token or an API key header).
  • The JSON body the API expects for a new post.

Step by step

  1. 1Write the body template

    Start from the JSON your API expects and put placeholders where the article values go. Each placeholder is replaced with the value, escaped for use inside a JSON string, so always put placeholders inside double quotes.

  2. 2Open the integration form in LazySEO

    • Open your project in LazySEO.
    • In the left sidebar, under Settings, click Integrations.
    • Click Add Integration.
    • In the window that says "Choose the type of integration you want to add.", click Custom API.
  3. 3Save the integration

    Fill in the fields as described in the table below, leave Enabled switched on (disabled integrations are skipped during publish) and click Add integration.

Fill in the LazySEO form

Labels below match the Custom API form in LazySEO. Fields marked required must be filled in before you can save.

Namerequired

Any name that helps you recognise this connection. Only shown inside LazySEO.

Example: Headless CMS API

Endpoint URLrequired

The URL that creates a post.

Example: https://api.example.com/articles

Methodrequired

POST, PUT or PATCH. The same method is used for new articles and for updates (see "Updates" below).

Example: POST

Headers (optional)

Authentication and any other headers your API needs, added with Add header. Content-Type: application/json is sent by default. Header values other than common non-secret ones are stored encrypted and hidden after saving.

Example: Authorization → Bearer sk_live_abc123

Body Template (optional)

The JSON body, with placeholders. If you leave it empty, LazySEO sends the same JSON payload as the Webhook.

Example: { "title": "{{title}}", "content": "{{content}}" }

Enabled

Leave on. When it is off, LazySEO skips this integration when publishing.

Test the connection

  • Right after you save, click Run quick test in the window, or test later from the integration card with Test.
  • Quick test sends a HEAD request (or GET if your server answers 405) to the Endpoint URL with your headers, waits up to 10 seconds, and passes on any status below 400. Many APIs answer 401, 404 or 405 to a GET on a create endpoint, so a failed Quick test does not always mean publishing will fail; the reason LazySEO shows says which status your API returned.
  • On the card's Test menu, Full test sends a real request with a test article titled "[TEST] LazySEO Test Article". Your template decides what is sent; there is no placeholder for the draft status, so the test post may be created as published in your system. Delete it afterwards.
  • Under Sync History on the Integrations page, each successful publish shows the request LazySEO sent and your API's response. A publish that still fails after all retries is listed with the error.

What gets published

WhatHow LazySEO handles it
RequestOne request per article to Endpoint URL with Method, Content-Type: application/json and your headers.
BodyYour Body Template (optional) with placeholders filled in, or the full webhook payload if the template is empty.
ContentThe article body ({{content}}): Markdown for articles as LazySEO generates them, HTML for articles edited and saved in the LazySEO editor. If your API expects HTML, it has to render the Markdown (see "Markdown or HTML" below).
TimeoutLazySEO waits up to 30 seconds for your API to answer a publish request, then counts it as failed and retries.
UpdatesIf your API returned an id the first time, publishing the same article again sends the request to Endpoint URL + / + that id, with the same method and body. Choose a method your API accepts for both, or expect a new post each time. If the post was deleted in your system, that update request fails (usually HTTP 404); LazySEO does not fall back to creating a new post.

Placeholders

Placeholders are written as {{name}}. Anything else in double curly braces is left as it is.

PlaceholderValue
{{title}}Article title
{{content}}Full article body: Markdown for generated articles, HTML for articles edited in the LazySEO editor
{{slug}}URL slug
{{metaDescription}}Meta description (empty if none)
{{featuredImageUrl}}Banner image URL (empty if none)

Example: a typical REST blog API

An API that creates posts at POST https://api.example.com/v1/posts, authenticates with a bearer token and expects the post inside a data object:

FieldValue
Endpoint URLhttps://api.example.com/v1/posts
MethodPOST
Headers (optional)Authorization → Bearer YOUR_API_TOKEN
Body Template (for an API that accepts Markdown)
{
  "data": {
    "title": "{{title}}",
    "slug": "{{slug}}",
    "body_markdown": "{{content}}",
    "excerpt": "{{metaDescription}}",
    "cover_image": "{{featuredImageUrl}}",
    "status": "published"
  }
}

Markdown or HTML

{{content}} is sent exactly as the article is stored in LazySEO. Generated articles are Markdown (GitHub style, possibly with embedded HTML blocks such as <figure> or a YouTube <iframe>); articles edited and saved in the LazySEO editor are HTML. Custom API does not convert between the two.

If your API stores Markdown, map {{content}} to its Markdown field. If it only accepts HTML, have it render Markdown with a library that allows embedded HTML (for example marked or markdown-it in Node.js, Parsedown in PHP), and pass content that already starts with an HTML tag such as <p> or <h2> through unchanged. If you can't change the API, use a Webhook receiver that converts the content and then calls your API.

Responses

  • Any 2xx status means success. Any other status is a failure, and the response body is shown as the error.
  • If the response has Content-Type: application/json, LazySEO reads the post id from a top-level id, externalId or postId field, and the public link from url, publishedUrl, link or permalink. Fields nested deeper (for example inside data) are not read.
  • The id can be a string or a number; LazySEO stores it as a string and uses it in the update URL.

Retries

Failed requests, including requests your API doesn't answer within 30 seconds, are retried like webhooks: up to 3 attempts a few seconds apart, and for scheduled auto-publishing up to 3 more rounds at least an hour apart. Make sure a repeated request doesn't create a duplicate post, for example by making slug unique in your API.

Troubleshooting

HTTP 400 or "invalid JSON" from your API.

Check that the template is valid JSON and every placeholder is inside double quotes, for example "{{content}}". Compare your template with what your API expects; the error shows your API's response.

HTTP 401 or 403.

Check the authentication header name and value in Headers (optional).

HTTP 404 or 405 when an article is published again.

Updates go to Endpoint URL + / + the returned id, with the same method. If your API uses a different method or path for updates, it will reject them. A 404 can also mean the post was deleted in your system since LazySEO first published it.

Quick test fails but Full test works.

That's normal for APIs that don't answer HEAD or GET on the create endpoint. Rely on the Full test.

Posts show ##, ** or - instead of headings, bold text and lists.

{{content}} is Markdown for generated articles. Send it to a Markdown field of your API, or render it to HTML on the API side (see "Markdown or HTML").

Publishing fails with "Timed out waiting for …".

Your API took longer than 30 seconds to answer. Make it reply as soon as the post is saved.

Saving the integration says "Please re-enter the credentials (password, token or secret headers) when changing the destination URL."

For security, saved secret headers are not reused for a new URL. Enter the header values again together with the new Endpoint URL.

Frequently asked questions

Can I send tags or topics through the body template?

Not with a placeholder: the template supports title, content, slug, metaDescription and featuredImageUrl. Leave the template empty to receive the full payload, which includes topics and tags.

Is {{content}} HTML or Markdown?

Markdown for articles as LazySEO generates them, and HTML for articles edited and saved in the LazySEO editor. LazySEO sends it as stored, without converting it.

Still stuck?

Email support@lazyseo.io with the error message from LazySEO and we'll help you connect.