With Claude Code or Cursor you edit real code, so you get full control over how you talk to a headless CMS. ElmapiCMS fits in via the ElmapiCMS JavaScript SDK or plain fetch: you add credentials to env vars, create a small CMS client (or call the API directly), and prompt Claude or Cursor to build pages that use it.
This guide walks you through getting ElmapiCMS credentials, understanding the correct API shape (from the official docs), where to put env vars, SDK vs fetch, and example prompts for blog lists and single posts.

Table of Contents
- Prerequisites
- Step 1: Get ElmapiCMS Credentials
- Step 2: How the ElmapiCMS Content API Works
- Step 3: Where to Add Env Vars
- Step 4: SDK Setup
- Step 5: Plain Fetch Alternative
- Step 6: Example Prompts for Blog Lists and Single Posts
- Tips and Gotchas
- Next Steps
Prerequisites
- Claude Code or Cursor and a project (Next.js, Remix, Astro, or similar).
- A running ElmapiCMS instance. You can use the demo to try things, or your own install (installation).
- A project in ElmapiCMS with at least one collection (e.g. “Posts”) and a few entries so you have real data. If you need to create a project and collection, see the getting started and collections docs.
Step 1: Get ElmapiCMS Credentials
ElmapiCMS uses project-scoped API access. Every request must include the project and, for private APIs, a token. You get these from the project’s API Access settings.
Where to find them
- Log in to your ElmapiCMS admin and open the project that has your content (e.g. your blog project).
- Go to Settings → API Access (see API Access in the docs).
- On that page you’ll see:
- Content API Endpoint – The base URL for all API calls, usually
https://your-domain.com/api. Copy this; you’ll use it asELMAPI_URL(or similar) in env. - Project ID – A UUID that identifies this project. Every request must send it in the
project-idheader.
- Content API Endpoint – The base URL for all API calls, usually
- If the project’s API is private (Public API is OFF), you need an access token:
- Click Create Token.
- Give it a name (e.g. “Cursor” or “Claude”) and tick the read ability.
- Copy the token immediately; it won’t be shown again. Store it in your password manager or env.
What you’ll use in code
- Base URL = Content API Endpoint (e.g.
https://cms.example.com/api). - Headers =
Accept: application/json,project-id(your project ID), and for private APIs:Authorization: Bearerfollowed by your token.
If your project has Public API enabled, you only need the project-id header for GET requests; you can omit the Authorization header. This guide assumes a private API and a token.
Step 2: How the ElmapiCMS Content API Works
The ElmapiCMS Content API is collection-based. The path is the collection slug (e.g. posts or blog-posts). Base URL + path + query = full URL.
Base URL and headers
- Base URL: The Content API Endpoint you copied (e.g.
https://your-domain.com/api). - Required headers:
Accept: application/jsonproject-id= your project ID- For private APIs:
Authorization: Bearer+ your token
Full details: Content API introduction.
List entries (e.g. blog posts)
Paginated: use paginate and page. The parameter name is paginate, not per_page.
GET /posts?paginate=10&page=1
Full URL example: https://your-domain.com/api/posts?paginate=10&page=1
Response: An object with data (array of entries), meta (current_page, last_page, total, etc.), and links (first, next, prev, last). See List Entries in the docs.
Without paginate: Omit paginate and you get a plain array of all entries.
Get one entry by slug
There is no dedicated “get by slug” path. You filter the list using the where parameter, then take the first (or only) item.
GET /posts?where[slug]=my-post-slug
Full URL example: https://your-domain.com/api/posts?where[slug]=my-post-slug
The response is either a single entry or a short array; use the first element. Filtering is described in Advanced Filtering.
Response shape
Each entry has:
uuid– Unique id.fields– Object with your collection’s fields (e.g.title,slug,content,excerpt).- Optionally
locale,published_at, etc.
So in code you’ll use entry.fields.title, entry.fields.slug, entry.fields.content, and so on.
Step 3: Where to Add Env Vars
Never hardcode the token or project ID. Put them in env and load them at build/runtime.
Next.js
- Local: Create
.env.localin the project root. AddELMAPI_URL,ELMAPI_API_KEY,ELMAPI_PROJECT_ID. Add.env.localto.gitignore(Next.js does this by default). - Vercel / Netlify / etc.: Set the same variables in the hosting dashboard so they’re available in server and edge code. Do not expose the token to the client if you can avoid it (e.g. call the CMS from API routes or server components only).
Other frameworks (Remix, Astro, etc.)
- Use the framework’s env convention (e.g.
.envor.env.local) and ensure the file is ignored by git. Useimport.meta.envorprocess.envas the framework expects, and keep the token out of client-side bundles.
When prompting Claude or Cursor
Tell it where the values live, e.g. “We use ElmapiCMS. Credentials are in env: ELMAPI_URL, ELMAPI_API_KEY, ELMAPI_PROJECT_ID. Use them only in server-side or API route code, not in client components.” That keeps the model from inventing wrong env names or putting secrets in the browser.
Step 4: SDK Setup
The ElmapiCMS JavaScript SDK wraps the Content API and handles headers for you.
Install
npm install @elmapicms/js-sdk
Env (example for Next.js)
In .env.local:
ELMAPI_URL=https://your-domain.com/api
ELMAPI_API_KEY=your-token-from-api-access
ELMAPI_PROJECT_ID=your-project-id-uuid
Create a CMS client
Create a small module (e.g. src/lib/cms.ts or lib/cms.js) so the rest of the app uses one client:
import { createClient } from '@elmapicms/js-sdk';
export const cms = createClient(
process.env.ELMAPI_URL!,
process.env.ELMAPI_API_KEY!,
process.env.ELMAPI_PROJECT_ID!
);
Use in code
- List posts:
cms.getEntries('posts', { sort: '-published_at', paginate: 10, page: 1 })(or whatever sort/options your collection uses). The SDK maps to the right query params. - Post by slug: Use the SDK’s filter option for slug (e.g.
cms.getEntries('posts', { filters: { slug: 'my-slug' } })) and take the first result. Check the SDK docs for the exact option name; it will correspond towhere[slug]=under the hood.
Exact method names may vary by SDK version; the npm package and Content API docs are the source of truth. The important part for prompting is: “We have a cms client from @elmapicms/js-sdk created with ELMAPI_URL, ELMAPI_API_KEY, ELMAPI_PROJECT_ID. Use it in server components or API routes only.”
Step 5: Plain Fetch Alternative
If you don’t use the SDK or the AI generates fetch calls, use the raw API with the right headers and query params.
List posts (paginated)
const baseUrl = process.env.ELMAPI_URL;
const projectId = process.env.ELMAPI_PROJECT_ID;
const token = process.env.ELMAPI_API_KEY;
const res = await fetch(
`${baseUrl}/posts?paginate=10&page=1`,
{
headers: {
'Accept': 'application/json',
'project-id': projectId!,
'Authorization': `Bearer ${token}`,
},
}
);
const json = await res.json();
const posts = json.data ?? json; // paginated returns { data }; sometimes a plain array
One post by slug
const res = await fetch(
`${baseUrl}/posts?where[slug]=${encodeURIComponent(slug)}`,
{
headers: {
'Accept': 'application/json',
'project-id': projectId!,
'Authorization': `Bearer ${token}`,
},
}
);
const json = await res.json();
const post = Array.isArray(json) ? json[0] : json.data?.[0] ?? json;
Use these only in server-side code or API routes so the token never reaches the client.
Step 6: Example Prompts for Blog Lists and Single Posts
Once env and the client (or fetch helpers) exist, you can prompt Claude or Cursor to build pages.
Blog list page
Add a /blog page that lists posts from ElmapiCMS. We have a cms client in src/lib/cms.ts created with createClient(ELMAPI_URL, ELMAPI_API_KEY, ELMAPI_PROJECT_ID). Use it in a server component to fetch posts (collection slug "posts"), then render each post’s fields.title, fields.slug, fields.excerpt and link to /blog/[slug]. Use the existing cms client; don’t add a new env or fetch from the client.
If you use fetch instead of the SDK:
Add a /blog page that lists posts from ElmapiCMS. We have env vars ELMAPI_URL, ELMAPI_PROJECT_ID, ELMAPI_API_KEY. In a server component or API route, call GET {ELMAPI_URL}/posts?paginate=10&page=1 with headers Accept: application/json, project-id: ELMAPI_PROJECT_ID, Authorization: Bearer ELMAPI_API_KEY. Parse the "data" array and render each entry’s fields.title, fields.slug, fields.excerpt with links to /blog/[fields.slug]. Do not use these env vars in client components.
Single post page
Add a page at /blog/[slug] that fetches one post by slug from ElmapiCMS. Use our cms client in src/lib/cms.ts (or the same env and fetch in a server component). Call the API with where[slug]=<slug> and use the first item. Render fields.title and fields.content. Keep all CMS calls server-side.
Homepage with latest posts
On the home page, show the latest 3 blog posts from ElmapiCMS. Use our cms client (or fetch with ELMAPI_* env) in a server component. Use paginate=3&page=1 or take the first 3 from the list. Display title, excerpt, and link to /blog/[slug] for each. No CMS token in the client bundle.
If the model invents wrong URLs or env names, remind it: “Use ELMAPI_URL, ELMAPI_API_KEY, ELMAPI_PROJECT_ID and the pattern /posts?paginate=10&page=1 for list, /posts?where[slug]=… for single post. All CMS requests must be server-side.”
Tips and Gotchas
- Token only on server: Use the CMS client or fetch only in server components, API routes, or other server-side code. Never ship the token to the browser.
- Headers on every request: Raw
fetchmust sendAccept: application/json,project-id, andAuthorization: Bearer+ token. Missing headers often cause 401 or empty responses. - Path = collection slug: The path is the collection slug only, e.g.
/posts. Full example:baseUrl + '/posts?paginate=10&page=1'. - Paginate vs per_page: The API uses query params
paginateandpage. Do not useper_page. - By slug: Use
where[slug]=<slug>on the list endpoint and take the first item. There is no separate “get by id/slug” URL. - Response shape: With
paginate, the body hasdata,meta, andlinks. Without it, you may get a plain array. Usedatawhen you requested pagination.
Next Steps
- API reference: Content API and List Entries for query options (
sort,where,locale, etc.). - API Access: API Access settings for project ID, endpoint, tokens, and public API.
- SDK: @elmapicms/js-sdk on npm.
- Other guides: Next.js starter template setup and simple blog with Next.js show a full ElmapiCMS + Next.js setup; Next.js portfolio with ElmapiCMS and Cursor walks through a Cursor-built site. Using ElmapiCMS with Lovable and Using ElmapiCMS with Bolt.new cover other AI builders.
Once env vars and a small CMS layer (SDK or fetch) are in place, you can iterate with Claude or Cursor to add or change pages and keep all ElmapiCMS access server-side.