Developers

Build on C1K

Every organization on c1k.me can be driven from code: a REST API for your own systems, and an MCP endpoint for AI assistants such as Claude, ChatGPT, Cursor and Codex. Both follow the same rules as the web interface, so a link made by a script or an assistant behaves exactly like one made by hand.

AI assistants (MCP)

Setup for Claude Code, Claude, ChatGPT, Cursor, VS Code, Codex and Gemini CLI, with an organization token or OAuth sign-in.

Examples

Small, complete programs in curl, JavaScript, Python and PHP. Each one runs unchanged in our test pipeline before every release.

Quick start

  1. Get a key. In your organization open Integrations, create a service account, then a key Used for: REST API with the scopes you need. The key is shown once; store it like a password. Your organization ID is shown on the same page.
  2. Keep both in environment variables, never in code:
Shell
export C1K_API_KEY='c1k_rest_...'
export C1K_ORG_ID='org_...'
  1. Check the key. /me answers for any valid key and lists what it may do.
curl
curl -sS "https://c1k.me/api/v1/orgs/$C1K_ORG_ID/me" \
  -H "Authorization: Bearer $C1K_API_KEY"
  1. Create a link. Only destination_url is required. The Idempotency-Key makes a retry after a lost response safe.
curl
curl -sS -X POST "https://c1k.me/api/v1/orgs/$C1K_ORG_ID/links" \
  -H "Authorization: Bearer $C1K_API_KEY" \
  -H "Idempotency-Key: c1k-$(date +%s)-$RANDOM" \
  -H 'Content-Type: application/json' \
  -d '{"destination_url":"https://venture.example/launch","title":"Launch page"}'

The answer carries data.short_url, for example https://c1k.me/Ab3xY9k, and the link's version.

Conventions

Base URLhttps://c1k.me/api/v1/orgs/{org_id}. A key works for exactly one organization; anything else answers 404.
AuthenticationAuthorization: Bearer c1k_rest_... only. Keys in URLs, query strings or cookies are refused.
Scopeslinks:read, links:write, links:delete, analytics:read, analytics:delete, exports:read, events:read, members:manage, integrations:manage. An operation outside the key's scopes answers 403.
FormatUTF-8 JSON, at most 65,536 bytes per request. Success: {"data": ..., "meta": {"request_id": ...}}. Times are RFC 3339 in UTC.
Safe retriesCreating links, bulk changes and import apply take an Idempotency-Key (16 to 128 characters). The same key with the same body returns the first result for 24 hours; a different body answers 409.
Concurrent editsEdits and deletions need If-Match with the current version (the ETag). Missing answers 428, outdated answers 412.
PagesLists return meta.next_cursor; pass it back as cursor. Up to 100 items per page.
Rate limits60 requests per minute per key, 300 per organization, 10 changes per minute per key. A 429 says how long to wait in Retry-After.

Errors

Every error has the same shape, with a stable code to branch on and a request_id to quote when you ask for help:

{"error": {"code": "invalid_input", "message": "Some fields are not valid.",
           "fields": [{"field": "destination_url", "code": "required", "message": "..."}],
           "request_id": "req_..."}}
Status and codeWhat to do
400 malformed_json, invalid_inputFix the request; fields names each problem.
401 unauthenticatedThe key is missing, wrong, expired or revoked.
403 forbiddenThe key lacks the scope, or the organization is suspended.
404 not_foundNo such object in this key's organization.
409 conflictA taken slug, a reused Idempotency-Key with another body, or a stale import receipt.
410 cursor_expiredThe event cursor fell out of the retained history; take a fresh snapshot.
412 version_conflict, 428 precondition_requiredRead the link again and send its current version in If-Match.
413, 415, 422Body too large, not JSON, or fields not valid.
429 rate_limitedWait for Retry-After seconds, then retry.
503, 507 storage_unavailableBusy or full; retry later. Short links keep redirecting.

Downloads