The v1.4.0 release ships JekCMS API v2 alongside the existing v1 endpoints, which remain active until v1.6.0. Key changes: bearer token authentication replaces API keys, cursor-based pagination replaces page-offset, and a unified response envelope replaces inconsistent per-endpoint formats.
Bearer token authentication uses short-lived access tokens (15-minute TTL) paired with long-lived refresh tokens (30-day TTL).
Obtain the initial token pair with POST /api/v2/auth/token, passing your API credentials in the request body. Include the access token as Authorization: Bearer {token} in subsequent requests. When an access token expires, exchange the refresh token for a new pair at POST /api/v2/auth/refresh. Do not store access tokens beyond the request cycle; store only the refresh token securely.
Cursor Pagination Fixes the Drift Problem
Cursor-based pagination replaces the ?page=N pattern. Each response that has additional results includes a next_cursor value in the meta object. Pass it as ?cursor={value} in the next request. This approach avoids the "page drift" difficulty: with offset pagination, inserting a new post while a client is paginating through results causes the client to skip or re-read posts. Cursor pagination is stable regardless of insertions or deletions.
Unified Response Envelope
The unified response envelope is {"success": true, "data": {...}, "meta": {...}, "errors": []}. In v1, each endpoint returned its own structure — some nested the main resource under a key matching the resource type ({"post": {...}}), others returned the resource directly at the root. v2 is consistent: the resource is always under data, pagination metadata is always under meta, and errors are always in the errors array.
Migration Strategy: One Endpoint at a Time
v1 endpoints remain active and will receive security patches until v1.6.0. If your integration uses offset pagination heavily — for example, to export large result sets — migrating to cursors requires rethinking how you handle result sets that exceed a single page. We recommend migrating one endpoint at a time, starting with read-only GET endpoints before tackling POST and PATCH endpoints that modify state.
Authentication: Code Examples
Obtaining an access token and using it in subsequent requests:
# Step 1: Get token pair
curl -X POST https://yoursite.com/api/v2/auth/token \
-H "Content-Type: application/json" \
-d '{"api_key": "your-key", "api_secret": "your-secret"}'
# Response:
# {"success": true, "data": {"access_token": "eyJ...", "refresh_token": "rf_...", "expires_in": 900}}
# Step 2: Use access token
curl https://yoursite.com/api/v2/posts \
-H "Authorization: Bearer eyJ..."
# Step 3: Refresh when expired (HTTP 401)
curl -X POST https://yoursite.com/api/v2/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refresh_token": "rf_..."}'
Token Storage Best Practices
Store refresh tokens in an encrypted database column or a secrets manager. Never log access or refresh tokens in application logs. In server-to-server integrations, implement automatic token rotation: request a new token pair when the access token is within 60 seconds of expiration, not after it has already expired. This avoids failed requests during the rotation window.
Cursor Pagination: Implementation Details
The cursor value is an opaque, base64-encoded string containing the sort key and record ID. Do not attempt to decode, construct, or modify cursor values. On a table with 100,000 posts, the v1 request ?page=500&per_page=20 takes approximately 1,200ms because MySQL must scan 10,000 rows before returning 20. The equivalent cursor request takes a consistent 15-25ms regardless of position in the result set.
Error Handling in v2
The v2 error format provides structured error information. Key error codes to handle in your integration:
AUTH_EXPIRED(HTTP 401): access token expired. Refresh and retry.AUTH_INVALID(HTTP 401): token is malformed or revoked. Re-authenticate from scratch.RATE_LIMITED(HTTP 429): too many requests. Check theRetry-Afterheader for seconds to wait.VALIDATION_FAILED(HTTP 422): request body failed validation. Check theerrorsarray for field-specific messages.NOT_FOUND(HTTP 404): resource does not exist or is not accessible with the current token scope.
v1 to v2 Endpoint Mapping
Reference for updating your integration endpoints:
GET /api/v1/postsbecomesGET /api/v2/postsPOST /api/v1/posts/createbecomesPOST /api/v2/posts(RESTful)POST /api/v1/posts/update/{id}becomesPATCH /api/v2/posts/{id}POST /api/v1/posts/delete/{id}becomesDELETE /api/v2/posts/{id}GET /api/v1/media/listbecomesGET /api/v2/media
The v2 endpoints follow RESTful conventions consistently: resource collections use plural nouns, individual resources use the collection name plus ID, and HTTP methods indicate the action.
Rate Limiting and Throttling
API v2 introduces per-token rate limiting. The default limits are 60 requests per minute for standard tokens and 300 requests per minute for admin tokens. Rate limit headers are included in every response:
X-RateLimit-Limit: maximum requests allowed per windowX-RateLimit-Remaining: requests remaining in current windowX-RateLimit-Reset: Unix timestamp when the window resets
When you hit the limit, the API returns HTTP 429 with a Retry-After header. For bulk operations like importing 10,000 posts from an external CMS, use the /api/v2/bulk/posts endpoint which accepts up to 100 records per request and counts as a single rate-limited call. This reduces a 10,000-request migration to 100 requests, completing in under 2 minutes rather than hitting rate limits for 3+ hours.