Skip to content
Get API key (opens in a new tab)

API v1

Authenticate, paginate, filter and include related data, and handle errors and rate limits, in the legacy Cevoid REST API at api.cevoid.com/v1.

Use this section when you call Cevoid's legacy REST API at https://api.cevoid.com/v1 directly. It covers API-specific behavior: authentication, rate limits, pagination, filtering, includes and errors. The reference lists the endpoints. For the Analytics SDK, MCP and other implementation guides, see the Developers overview, and for using Cevoid in the platform, see the Help Center.

Note:

API v1 (legacy) means only api.cevoid.com/v1. New integrations use API v2. The Storefront API also has /v1 paths, but it is a separate, current API.

Authentication

Every API v1 endpoint requires an API key. Include it in the x-api-key header of each request.

Get an API key

  1. Log in to your Cevoid account
  2. Go to Settings → Integrations → Cevoid API⁠
  3. Create an API key with the scopes your integration needs, and copy it when it is shown

A new key is shown only once, when you create it.

Legacy and scoped keys

API v1 accepts two kinds of key:

  • Scoped secret keys start with cev_sk_. They are the keys API v2 uses. On API v1, a scoped key needs posts:read for the post and inbox endpoints and members:read for GET /v1/members. The presets * and *:read include both, and the market endpoints need no scope. See Secret key scopes.
  • Legacy v1 keys keep full v1 access until you revoke them. You can no longer create them: existing legacy keys are listed under Legacy keys on the same settings page, where you can revoke them. Legacy keys do not work with API v2.

Send the API key

Include your API key in the request headers using the x-api-key field:

curl https://api.cevoid.com/v1/posts \
  -H "x-api-key: YOUR_API_KEY"
Success:

If your request is properly authenticated, you receive a successful response with the requested data.

Keep keys secure

Warning:

Never commit your Cevoid API key to version control systems like GitHub. Store your API keys securely and treat them like passwords.

  • Store API keys as environment variables
  • Use different API keys for development and production environments
  • Rotate your API keys regularly
  • Restrict API key access to only necessary team members

Rate limits

To ensure fair usage and keep the API stable for all users, API v1 enforces rate limits. When you exceed a limit, the API returns a 429 Too Many Requests response.

Limits per key and per company

BudgetApplies toRateBurst
Per API keyEvery endpoint200 requests per second400 requests
Per companyGET /v1/inbox, GET /v1/posts/{id} and GET /v1/collections/{id}/posts, each with its own budget20 requests per second40 requests

A request must pass every budget that applies to it. Requests served from a cache still count.

Warning:

Rate limits are applied per API key, so applications that share a key share its limit. The per-company budgets are shared by all of your API keys: creating more keys does not add capacity.

Note:

Need a higher rate limit? Contact support to discuss your requirements.

Rate limit headers

Responses include headers that help you monitor your usage. They follow the IETF RateLimit header fields:

HeaderDescription
RateLimit-LimitBurst capacity of the budget: 400 for the API key, or 40 for a per-company budget
RateLimit-RemainingWhole requests left in that budget
RateLimit-ResetSeconds to wait before retrying a 429; 0 on successful requests
retry-afterSeconds to wait before making another request (only on 429 responses)

Handle rate limit errors

When you exceed a limit, you receive a 429 status code with a plain-text body:

429 Too Many Requests
HTTP/1.1 429 Too Many Requests
content-type: text/plain; charset=utf-8
RateLimit-Limit: 400
RateLimit-Remaining: 0
RateLimit-Reset: 1
retry-after: 1

Rate limit exceeded

The retry-after header tells you how long to wait before retrying.

Monitor rate limit headers

Check the RateLimit-Remaining header to slow down before you reach the limit:

const response = await fetch('https://api.cevoid.com/v1/posts', {
  headers: { 'x-api-key': 'YOUR_API_KEY' }
});

const remaining = response.headers.get('RateLimit-Remaining');

if (parseInt(remaining) < 2) {
  console.log('Low on requests. Slow down before the next request.');
}

Retry after the delay

Wait for the retry-after delay before you retry, and cap the number of attempts:

async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.status === 429) {
      const retryAfter = parseInt(response.headers.get('retry-after') || '1');
      console.log(`Rate limited. Retrying in ${retryAfter} seconds...`);
      await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
      continue;
    }

    return response;
  }

  throw new Error('Max retries exceeded');
}

Cache responses

Reduce API calls by caching responses when possible:

const cache = new Map();
const CACHE_TTL = 60000; // 1 minute

async function getCachedPosts(galleryId) {
  const cacheKey = `posts-${galleryId}`;
  const cached = cache.get(cacheKey);

  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
    return cached.data;
  }

  const response = await fetch(`https://api.cevoid.com/v1/galleries/${galleryId}/posts`, {
    headers: { 'x-api-key': 'YOUR_API_KEY' }
  });

  const data = await response.json();
  cache.set(cacheKey, { data, timestamp: Date.now() });

  return data;
}

Pagination

API v1 uses pagination to limit the data returned in a single request, which makes large datasets easier to work with. All endpoints that return multiple items support pagination.

Pagination parameters

ParameterTypeDefaultDescription
limitinteger25Number of items returned per request, from 1 to 25
skipinteger0Number of items to skip from the beginning of the result set

A limit above 25 returns 400 with the body limit: less than or equal to 25.

Make paginated requests

curl -G https://api.cevoid.com/v1/posts \
  -H "x-api-key: YOUR_API_KEY" \
  -d skip=0 \
  -d limit=10

Change skip and limit to request another part of the result set:

curl -G https://api.cevoid.com/v1/posts \
  -H "x-api-key: YOUR_API_KEY" \
  -d skip=5 \
  -d limit=15

Paginated response format

Paginated responses include metadata to navigate through the dataset:

{
  "count": 30,
  "next": "https://api.cevoid.com/v1/posts?skip=15&limit=10",
  "nodes": [
    {
      "id": "507f1f77bcf86cd799439011",
      // ... post data
    },
    {
      "id": "507f191e810c19729de860ea",
      // ... post data
    }
    // ... more items
  ]
}
  • count: total number of items available
  • next: URL for the next page of results, or null on the last page
  • nodes: array containing the requested items

Pagination best practices

Tip:

Use the next field from the response to get the URL for the next page of results. This ensures you use the correct parameters.

  1. Start with the default limit: 25 items, which is also the maximum
  2. Use the next URL: don't construct pagination URLs by hand
  3. Handle empty results: check whether nodes is empty to detect the end of results
  4. Respect rate limits: don't make requests too quickly when you page through large datasets

Iterate through all results

let skip = 0;
const limit = 25; // Use maximum for efficiency
let hasMoreData = true;

while (hasMoreData) {
  const response = await fetch(`https://api.cevoid.com/v1/posts?skip=${skip}&limit=${limit}`, {
    headers: {
      'x-api-key': 'YOUR_API_KEY'
    }
  });

  const data = await response.json();

  // Process the current batch
  data.nodes.forEach(post => {
    // Handle each post
  });

  // Check if there are more results
  hasMoreData = data.nodes.length === limit;
  skip += limit;
}
Warning:

The maximum limit is 25 items per request. A request with a higher limit returns 400.

Filtering

Filtering helps you retrieve specific subsets of data. Filters narrow down results based on criteria such as label associations. GET /v1/posts and GET /v1/products/{id}/posts accept a filter parameter.

Filter structure

Pass filters as a URL-encoded JSON string containing an array of filter objects. Each filter object must include three fields:

  • type: the type of filter to apply. Only label is supported: it filters posts by their associated labels.
  • operator: how to combine multiple values. OR returns posts matching any of the values, and AND returns posts matching all of them.
  • values: array of IDs to filter by

Filter by one label

curl -G https://api.cevoid.com/v1/posts \
  -H "x-api-key: YOUR_API_KEY" \
  --data-urlencode 'filter=[{"type":"label","operator":"OR","values":["507f1f77bcf86cd799439011"]}]'

Filter by any of several labels

Returns posts that have at least one of the specified labels:

curl -G https://api.cevoid.com/v1/posts \
  -H "x-api-key: YOUR_API_KEY" \
  --data-urlencode 'filter=[{"type":"label","operator":"OR","values":["507f1f77bcf86cd799439011","507f191e810c19729de860ea"]}]'

Filter by all of several labels

Returns posts that have all of the specified labels:

curl -G https://api.cevoid.com/v1/posts \
  -H "x-api-key: YOUR_API_KEY" \
  --data-urlencode 'filter=[{"type":"label","operator":"AND","values":["507f1f77bcf86cd799439011","507f191e810c19729de860ea"]}]'

Get label IDs

To filter by label, you need each label's ID:

  1. Log in to your Cevoid account
  2. Go to Settings → Labels⁠
  3. Find the label you want to filter by
  4. Open the label's menu and choose Copy ID

Label IDs are 24-character hexadecimal strings. GET /v1/posts also returns the ID of each post's labels when you request include=labels. See Includes.

Filter errors

An invalid filter returns 400 with a plain-text body:

Response bodySolution
A JSON parse error, such as Unexpected token 'o', "not-json" is not valid JSONMake sure the filter is valid JSON and properly URL-encoded.
Filter param needs to be an arrayThe filter parameter must be a JSON array, even if you filter by one label.
Type is required, Operator is required or Values is requiredEach filter object must include type, operator and values.
Filter type category is not supportedOnly label is supported as a filter type.
Filter operator > is not supportedOnly the OR and AND operators are supported.
Filter values must be an arrayvalues must be an array of IDs.
Filter value "invalid-id" is not a valid IDEvery value must be a valid ID: a 24-character hexadecimal string.
Warning:

Always URL-encode the filter parameter when making requests. Invalid JSON or incorrectly formatted IDs return 400 errors.

Includes

Use the include query parameter when you need related data alongside the resources you request. You get that data in the same response, and responses stay smaller when you do not need it.

Each endpoint's reference lists the relationships it supports and the fields they add. Includes are only available on endpoints that document the include parameter.

Supported routes

EndpointSupported includes
GET /v1/postslabels

Include query format

Pass include once, using a comma-separated list of supported relationship names. Whitespace around names is ignored; repeating a name does not duplicate the included data.

Unsupported names, empty values and repeated include query parameters return 400. Use only the names listed in the endpoint's reference.

Include response behavior

Includes add related data to each returned resource. They do not change which resources match your request. When you omit include, you get the endpoint's default response.

The endpoint's response schema describes the included fields and what to expect when related data is unavailable.

Paginate with includes

Follow the response's next URL to keep includes on subsequent pages. See Pagination for the response format.

Errors

API v1 uses standard HTTP status codes to indicate the success or failure of your requests. A successful request returns 200 with a JSON body. An error returns a plain-text body that describes the problem, not JSON.

Status codes

StatusExample bodyMeaning and solution
400limit: less than or equal to 25A parameter is invalid. The body names each invalid parameter and the rule it broke, separated by commas.
400Could not find galleryA gallery, product, member, label, collection or market in the request does not exist. Check the ID.
401No valid API key providedThe API key is missing or invalid. Check the key and that you send it in the x-api-key header.
403You are not allowed to access this resourceThe key lacks the scope this endpoint needs, or your plan does not include API access.
404Could not find postGET /v1/posts/{id} or GET /v1/markets/{id} found no post or market with that ID.
429Rate limit exceededWait for the retry-after delay. See Rate limits.
500Something went wrongAn error occurred on Cevoid's servers. Retry with backoff.

Check the status code

const response = await fetch('https://api.cevoid.com/v1/posts', {
  headers: {
    'x-api-key': 'YOUR_API_KEY'
  }
});

if (!response.ok) {
  const message = await response.text();
  console.error(`API Error (${response.status}):`, message);
  return;
}

const data = await response.json();

Retry server errors

Tip:

For 500 responses, retry with exponential backoff. These are typically temporary server issues.

async function retryRequest(url, options, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);

      if (response.ok || response.status < 500) {
        return response; // Success or client error (don't retry)
      }

      if (attempt === maxRetries) {
        throw new Error(`Request failed after ${maxRetries} attempts`);
      }

      // Wait before retrying (exponential backoff)
      await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, attempt - 1)));

    } catch (error) {
      if (attempt === maxRetries) throw error;
    }
  }
}

Log errors for debugging

if (!response.ok) {
  const message = await response.text();

  // Log the error with context
  console.error('API Request Failed:', {
    status: response.status,
    message,
    requestUrl: response.url,
    timestamp: new Date().toISOString()
  });
}
Warning:

Never expose API keys or sensitive information in error logs or user-facing error messages.

Reference

The reference is generated from the OpenAPI specification. Download it to generate a client or import the API into your HTTP tool.