---
title: Quickstart
description: From a key to real content in three requests. Nothing to install.
---

There is no SDK and no build step. A key and `curl` are the whole toolchain.

## 1. Get a key

Create a project at [dashboard.zevcontent.com](https://dashboard.zevcontent.com),
then **Settings → API keys**. You are offered two kinds; take a **secret** key
for now, because you are calling from a terminal.

```bash
export ZEVCONTENT_KEY=sk_...
```

The key selects the project. There is no project id in any URL, so the same
path serves different content for different keys.

> **WARNING: Which key goes where**
>
> A secret `sk_…` key is server-side only. The moment this reaches a browser
> bundle it is public, and anyone holding it can read everything the project
> serves. Browsers get a publishable `pk_…` key instead, locked to your origins.
> [API keys](/guide/keys) explains the difference in one page.

## 2. Ask the project what it holds

```http
GET /v1/introspection
```

```bash
curl https://api.zevcontent.net/v1/introspection \
  -H "Authorization: Bearer $ZEVCONTENT_KEY"
```

This is the single most useful request in the API. It returns every content
type with its fields and the exact shape each response will have, every
built-in module with its endpoints, the rich text contract, and the rules for
authentication, paging, filtering, rate limits and errors.

It describes **your** project, so it cannot be out of date with the content you
are about to fetch. [Introspection](/api/introspection) covers what comes back.

## 3. Read something

The built-in modules work immediately, with nothing configured:

```bash
curl "https://api.zevcontent.net/v1/blog/articles?limit=5" \
  -H "Authorization: Bearer $ZEVCONTENT_KEY"
```

```json
{
  "data": [
    {
      "slug": "how-we-deploy",
      "title": "How we deploy",
      "excerpt": "A short tour of the pipeline.",
      "authors": [{ "id": "aut_…", "name": "Ada", "slug": "ada" }],
      "categories": [{ "id": "cat_…", "name": "Engineering", "slug": "engineering" }],
      "reading_time_minutes": 6,
      "published_at": "2026-08-14T09:00:00.000Z",
      "seo": { "title": "How we deploy", "og_image": { "url": "https://…" } }
    }
  ],
  "meta": { "module": "blog", "limit": 5, "has_more": false, "next_cursor": null }
}
```

Note what you did **not** have to do: no second request for the byline, no
second request for the category, and no fallback chain to build `seo` yourself.

Your own types work the same way, at `/v1/content/{type}`:

```bash
curl "https://api.zevcontent.net/v1/content/team-member?where[role]=engineer&sort=name" \
  -H "Authorization: Bearer $ZEVCONTENT_KEY"
```

## 4. Page properly

Every listing is paginated, and there is no way to ask for all of something.
Read both `meta.next_offset` and `meta.next_cursor`: exactly one is set on a
page that has a successor, and **both are null on the last page**.

```bash
# The loop that works against every listing here.
next=0
while [ -n "$next" ]; do
  page=$(curl -s "https://api.zevcontent.net/v1/kb/articles?limit=50&offset=$next" \
    -H "Authorization: Bearer $ZEVCONTENT_KEY")
  echo "$page" | jq '.data[].title'
  next=$(echo "$page" | jq -r '.meta.next_offset // empty')
done
```

Do not compute the next offset yourself, and do not loop on `has_more` alone.
[Pagination and filtering](/api/pagination) explains why the two styles exist.

## Next

- [API keys](/guide/keys): which key, and where it is safe.
- [Content types and entries](/concepts/content-model): modelling your own shapes.
- [API overview](/api/): the whole surface on one page.