Tell us about your site and we'll let you in when your spot opens up.
everydayseo is a Contentful-style headless content source: you write and optimize ranking-ready articles here, your site pulls the published ones over a read-only API. One install, npm i @everydayseo/node, and every framework below fetches your articles with real, copy-paste code.
The SDK is read-only and has zero runtime dependencies (it uses global fetch on Node 18+ or any modern browser). It pulls your published articles out, it never publishes.
npm install @everydayseo/nodeThen create one client and list your articles. Paginate by passing the returned cursor back until hasMore is false.
import { createClient, SORT_BY_PUBLISHED_AT } from "@everydayseo/node";
const client = createClient({
token: process.env.EVERYDAYSEO_TOKEN!, // pk_... (browser) or sk_... (server)
workspaceId: process.env.EVERYDAYSEO_WORKSPACE_ID!,
});
// Newest published first. Keyset pagination via the returned cursor; responses
// are server-side cached (~60s), so repeat reads skip the origin.
const { items, cursor, hasMore } = await client.listArticles({
limit: 100,
sortedBy: SORT_BY_PUBLISHED_AT,
});
// Fetch one article + its SEO block. getArticle({ slug }) is the ergonomic form;
// getArticleBySlug(slug) still works if you prefer it.
const { article, seo } = await client.getArticle({ slug: items[0].slug! });
console.log(article.title, article.publishedAt, seo.metaHtml); // seo.metaHtml -> <head>Mint a delivery token in the dashboard under Workspace → API keys. Send it as Authorization: Bearer <token>. There are two kinds:
EDITOR secret key may pass { preview: true } to include drafts; pk_ and viewers never see drafts.Base URL defaults to https://api.everydayseo.com (use https://api-beta.everydayseo.com for beta).
Runtime frameworks fetch at render or build time. Static generators with no runtime JS use a small Node prebuild script that writes markdown into your content directory. Pick your stack.
App Router server components fetch at request/build time. A blog index sorted by publishedAt, and a [slug] page whose <head> comes from the delivery API's SEO block. Set an ISR revalidate (or use getStaticProps on the Pages Router); delivery responses are server-side cached, and your build caches again on top.
import { createClient } from "@everydayseo/node";
// One shared client for the whole app. Reads run server-side, so a secret (sk_)
// key is fine; a publishable (pk_) key works too.
export const everydayseo = createClient({
token: process.env.EVERYDAYSEO_TOKEN!,
workspaceId: process.env.EVERYDAYSEO_WORKSPACE_ID!,
});import Link from "next/link";
import { SORT_BY_PUBLISHED_AT } from "@everydayseo/node";
import { everydayseo } from "../../lib/everydayseo";
// ISR: re-fetch at most once a minute. Delivery responses are also
// server-side cached (~60s), so repeat reads skip the origin.
export const revalidate = 60;
// Server component: newest published articles first.
export default async function BlogIndex() {
const { items } = await everydayseo.listArticles({
limit: 20,
sortedBy: SORT_BY_PUBLISHED_AT,
});
return (
<main>
<h1>Blog</h1>
<ul>
{items.map((article) => (
<li key={article.id}>
<Link href={`/blog/${article.slug}`}>{article.title}</Link>
{article.publishedAt ? (
<time dateTime={article.publishedAt}>
{new Date(article.publishedAt).toLocaleDateString()}
</time>
) : null}
{article.excerpt ? <p>{article.excerpt}</p> : null}
</li>
))}
</ul>
</main>
);
}import type { Metadata } from "next";
import { notFound } from "next/navigation";
import { EverydaySeoApiError } from "@everydayseo/node";
import { everydayseo } from "../../../lib/everydayseo";
type Props = { params: { slug: string } };
// Generate <head> metadata from the delivery API's SEO block.
export async function generateMetadata({ params }: Props): Promise<Metadata> {
try {
// getArticle({ slug }) is the ergonomic form; getArticleBySlug(slug)
// still works if you prefer it.
const { article, seo } = await everydayseo.getArticle({ slug: params.slug });
const meta = (seo.meta ?? {}) as { title?: string; description?: string };
return {
title: meta.title ?? article.title,
description: meta.description ?? article.metaDescription ?? undefined,
};
} catch {
return {};
}
}
export default async function ArticlePage({ params }: Props) {
let data;
try {
data = await everydayseo.getArticle({ slug: params.slug });
} catch (err) {
if (err instanceof EverydaySeoApiError && err.status === 404) notFound();
throw err;
}
const { article } = data;
return (
<article>
<h1>{article.title}</h1>
{/* The API returns rendered, trusted HTML for the body. */}
<div dangerouslySetInnerHTML={{ __html: article.contentHtml ?? "" }} />
</article>
);
}Lovable, Bolt, and v0 consume everydayseo the same way any app does: they fetch the Content Delivery API. No GitHub sync required. This is the ship-now Custom-API recipe that needs nobody's permission.
EVERYDAYSEO_TOKEN (pk_ or sk_) and EVERYDAYSEO_WORKSPACE_ID as Cloud Secrets. Because our API needs an auth header, Lovable creates an Edge Function to protect the key.GET /v1/workspaces/{id}/articles with the Bearer header and returns the list to your app./articles/by-slug/{slug} powers the detail pages. Bolt and v0 use the exact same fetch in their generated app (v0 can also wire GitHub + a Vercel deploy hook).Use everydayseo as my content source.
Store my EVERYDAYSEO_TOKEN and EVERYDAYSEO_WORKSPACE_ID as Cloud Secrets, then
add an Edge Function that fetches
GET https://api.everydayseo.com/v1/workspaces/{workspaceId}/articles?limit=20
with an "Authorization: Bearer {EVERYDAYSEO_TOKEN}" header.
Render the returned articles as a blog index, with /blog/[slug] detail pages
that call /articles/by-slug/{slug} and render article.contentHtml.// supabase/functions/everydayseo-articles/index.ts
// Lovable Cloud auto-generates an Edge Function like this so your everydayseo
// key stays server-side (never shipped to the browser).
Deno.serve(async () => {
const token = Deno.env.get("EVERYDAYSEO_TOKEN"); // from Lovable Cloud Secrets
const workspaceId = Deno.env.get("EVERYDAYSEO_WORKSPACE_ID");
const res = await fetch(
`https://api.everydayseo.com/v1/workspaces/${workspaceId}/articles?limit=20`,
{ headers: { Authorization: `Bearer ${token}` } }
);
const data = await res.json(); // { articles: { items, hasMore, nextCursor } }
return new Response(JSON.stringify(data.articles), {
headers: { "Content-Type": "application/json" },
});
});Every route is scoped to your workspace and returns a single named resource. The SDK wraps these for you, but you can call them directly from any language.
/v1/workspaces/{workspaceId}/articlesList published article summaries (no body). Query: limit, cursor, updatedSince, preview, status, sortedBy (publishedAt, createdAt, updatedAt)./v1/workspaces/{workspaceId}/articles/by-slug/{slug}Full article + SEO block (meta, jsonLd, hreflang, metaHtml) by slug./v1/workspaces/{workspaceId}/articles/{articleId}Full article + SEO block by id./v1/workspaces/{workspaceId}/articles/{articleId}.mdRendered markdown export (text/markdown)./v1/workspaces/{workspaceId}/articles/{articleId}.htmlRendered HTML export (text/html).Responses are allowlisted: internal fields (workspace ids, prompts, quality signals, internal links) never reach the wire.
Reads are server-side cached (~60s per workspace + query), and a build-time fetch caches again in getStaticProps / ISR (Astro, Nuxt, and Eleventy prebuilds too), so users hit static output, not this API.
everydayseo researches, writes, and optimizes your articles for AI search. Your site pulls the published ones with one SDK install. Start free.
Get your delivery tokenNo credit card required. Read-only SDK, published articles only.