Coworkies API Reference

REST API for the coworking, workspace, and hospitality job board, space directory, and community platform.

Base URL https://api.coworkies.com/v2

Use the Coworkies API to:

  • Post and manage remote job listings programmatically
  • Search coworking spaces across 100+ cities
  • Build Chrome extensions and AI agent integrations
  • Access blog content and community resources

All endpoints return JSON. Authenticated endpoints require an API key sent via header, bearer token, or query parameter.

Machine-Readable Resources

OpenAPI Spec OpenAPI 3.1 specification for code generation, Swagger UI, and Postman import
llms.txt Concise API overview optimized for AI agent context windows
llms-full.txt Complete API reference with all endpoints, schemas, and examples for LLMs

Authentication

All requests (except public endpoints) require an API key. Pro space owners can create and manage keys from the API Keys section in their space dashboard.

Key format

API keys use prefixed tokens for easy identification:

  • cwk_live_ — Live key with read & write access
  • cwk_rstr_ — Restricted key, read-only access

Sending your key

Header recommended X-API-Key: cwk_live_...
Bearer token Authorization: Bearer cwk_live_...
Query parameter ?api_key=cwk_live_...

Scopes

Each API key is assigned permission scopes that control what it can access.

ScopeAccess
readRead all resources (jobs, spaces, cities, events, blog)
writeRead & write all resources. Implies read.
jobs:readRead job listings only
jobs:writeCreate & update job listings
spaces:readRead space profiles
events:readRead events

Space-scoped keys

Keys created from a space dashboard are automatically scoped to that space. They will only return data belonging to that space (jobs, profile, events). Attempting to access or modify another space’s data returns 403 Forbidden.

Key security: Full API keys are shown only once at creation. Store them securely — we cannot retrieve a lost key. Revoke and create a new one if needed.

Public endpoints (no auth required)

  • GET /v2 — API info and endpoint listing
  • POST /v2/feedback — Submit feedback
  • POST /v2/newsletters/subscribe — Newsletter signup
Request examples
# Recommended: X-API-Key header
curl https://api.coworkies.com/v2/jobs \
  -H "X-API-Key: your_api_key"

# Alternative: Bearer token
curl https://api.coworkies.com/v2/jobs \
  -H "Authorization: Bearer your_api_key"
const response = await fetch('https://api.coworkies.com/v2/jobs', {
  headers: { 'X-API-Key': 'your_api_key' }
});
const data = await response.json();
import requests

response = requests.get(
    'https://api.coworkies.com/v2/jobs',
    headers={'X-API-Key': 'your_api_key'}
)
data = response.json()
<?php
$ch = curl_init('https://api.coworkies.com/v2/jobs');

curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'X-API-Key: your_api_key',
    ],
]);

$response = curl_exec($ch);
curl_close($ch);

$data = json_decode($response, true);

Response Format

All responses use a consistent JSON envelope with success (boolean) and status (HTTP code) fields.

Successful responses include a data object. Error responses include an error object with code, message, and optionally details (array of validation messages).

Success response
{
  "success": true,
  "status": 200,
  "data": {
    "jobs": [...],
    "pagination": { ... }
  }
}
Error response
{
  "success": false,
  "status": 422,
  "error": {
    "code": "validation_error",
    "message": "Validation failed",
    "details": [
      "Job title is required",
      "City ID is required"
    ]
  }
}

Error Handling

Errors follow HTTP conventions. The error.code field provides a machine-readable identifier for programmatic handling.

Status Code Meaning
400bad_requestMalformed request body or invalid JSON
401unauthorizedMissing or invalid API key
403forbiddenValid key but insufficient permissions
404not_foundEndpoint or resource not found
405method_not_allowedHTTP method not supported on this endpoint
409conflictResource conflict (e.g. duplicate)
422validation_errorValidation failed (see error.details array)
429rate_limit_exceededToo many requests — back off and retry
500server_errorInternal server error
Error handling example
const response = await fetch('https://api.coworkies.com/v2/jobs', {
  headers: { 'X-API-Key': 'your_api_key' }
});

const json = await response.json();

if (!json.success) {
  console.error(`${json.error.code}: ${json.error.message}`);
  if (json.error.details) {
    json.error.details.forEach(d => console.error(`  - ${d}`));
  }
}
<?php
$ch = curl_init('https://api.coworkies.com/v2/jobs');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ['X-API-Key: your_api_key'],
]);

$response = curl_exec($ch);
curl_close($ch);

$json = json_decode($response, true);

if (!$json['success']) {
    error_log(sprintf(
        '%s: %s',
        $json['error']['code'],
        $json['error']['message']
    ));
    if (!empty($json['error']['details'])) {
        foreach ($json['error']['details'] as $detail) {
            error_log("  - {$detail}");
        }
    }
}
import requests

response = requests.get(
    'https://api.coworkies.com/v2/jobs',
    headers={'X-API-Key': 'your_api_key'}
)

data = response.json()

if not data['success']:
    print(f"{data['error']['code']}: {data['error']['message']}")
    for detail in data.get('error', {}).get('details', []):
        print(f"  - {detail}")

Pagination

By default, GET /v2/jobs returns all active jobs with a total count. When pagination is enabled server-side, use page and per_page query parameters.

ParamTypeDescription
pageintPage number (default: 1)
per_pageintItems per page, max 50 (default: 20)
Paginated response
{
  "data": {
    "jobs": [...],
    "pagination": {
      "total": 142,
      "limit": 20,
      "offset": 0,
      "current_count": 20,
      "total_pages": 8,
      "current_page": 1,
      "has_next": true,
      "has_previous": false
    }
  }
}

Rate Limiting

Default limits per API key:

  • 60 requests/minute
  • 1,000 requests/hour

Verified integrations may receive higher limits (120 requests/minute).

When rate limited, you'll receive a 429 response. Implement exponential backoff with jitter for retries.

Jobs

List Jobs

GET /v2/jobs

Returns active job postings with filtering, search, and optional pagination.

Query Parameters

ParamTypeDescription
pageintPage number (default: 1)
per_pageintItems per page, max 50 (default: 20)
qstringSearch title, company, or tags
job_typestringfulltime parttime freelance internship
remoteboolFilter remote-only jobs (1 or true)
citystringFilter by city slug (e.g. berlin)
Request
curl "https://api.coworkies.com/v2/jobs?q=developer&remote=1&per_page=5" \
  -H "X-API-Key: your_api_key"
const params = new URLSearchParams({
  q: 'developer',
  remote: '1',
  per_page: '5'
});

const response = await fetch(
  `https://api.coworkies.com/v2/jobs?${params}`,
  { headers: { 'X-API-Key': 'your_api_key' } }
);

const { data } = await response.json();
import requests

response = requests.get(
    'https://api.coworkies.com/v2/jobs',
    headers={'X-API-Key': 'your_api_key'},
    params={'q': 'developer', 'remote': '1', 'per_page': '5'}
)

data = response.json()['data']
<?php
$params = http_build_query([
    'q' => 'developer',
    'remote' => '1',
    'per_page' => '5',
]);

$ch = curl_init("https://api.coworkies.com/v2/jobs?{$params}");

curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'X-API-Key: your_api_key',
    ],
]);

$response = curl_exec($ch);
curl_close($ch);

$data = json_decode($response, true)['data'];
Response
{
  "success": true,
  "status": 200,
  "data": {
    "jobs": [
      {
        "id_job": 1234,
        "title": "Remote Software Developer",
        "company": "Tech Solutions Inc.",
        "job_type": "fulltime",
        "tags": "javascript,react,node",
        "is_remote": true,
        "page_url": "remote-software-developer-1234",
        "external_url": "https://example.com/apply?ref=coworkiesjobs",
        "apply_url": "https://example.com/careers/apply",
        "company_logo": "logo_1234.png",
        "date_added": "2026-05-15T10:30:00Z",
        "expiration_date": "2026-06-15T00:00:00Z",
        "city_name": "Berlin",
        "city_slug": "berlin",
        "space_name": null,
        "sal_min": "65000",
        "sal_max": "85000",
        "currency": "EUR",
        "url": "https://www.coworkies.com/jobs/remote-software-developer-1234/details"
      }
    ],
    "pagination": {
      "total": 42,
      "current_page": 1,
      "has_next": true
    }
  }
}

Create Job

POST /v2/jobs

Create a new job posting. Requires jobs:write or write scope.

The job is automatically assigned to your API key’s space. Job status and publishing behavior depends on your space’s plan.

Deduplication: A fingerprint is computed from normalized (title + company + city + external_url). If a matching job exists, it is updated instead of duplicated. The response action field indicates "created" or "updated".

Request Body (JSON)

FieldTypeDescription
title requiredstringJob title
company requiredstringCompany name
description requiredstringJob description (plain text or basic HTML: p, br, ul, ol, li, h2, h3, strong, em, a)
job_type requiredstringfulltime parttime freelance internship
id_city required*intCity ID from GET /v2/cities. Required unless city_name provided.
city_name required*stringCity name (e.g. "Berlin"). Resolved to id_city server-side. Use this OR id_city.
tags requiredstringComma-separated tags (e.g. "react,node,remote"). Normalized: trimmed, lowercased, deduplicated.
external_url optionalstringLink to original job listing source. ?ref=coworkiesjobs is appended automatically.
apply_url optionalstringDirect application link for candidates (separate from source URL).
is_remote optionalint0 or 1 (default: 0)
inv_email optionalstringContact email for notifications. Must be valid email format.
currency optionalstringEUR USD GBP (default: EUR)
sal_min optionalintMinimum annual salary
sal_max optionalintMaximum annual salary
id_space optionalintAssociated coworking space ID (must be confirmed)
id_plan optionalintPricing plan ID (default: 5 = free). Must be active.
City resolution: Pass either id_city (integer) or city_name (string). The API tries exact match first, then prefix match. The resolved city is returned as city.id and city.name.
Validation: sal_min cannot exceed sal_max. Currency must be EUR, USD, or GBP. The external_url is validated and gets ?ref=coworkiesjobs appended. Invalid inv_email falls back to a default. Status is always draft (server-enforced).
Request
curl https://api.coworkies.com/v2/jobs \
  -X POST \
  -H "X-API-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Community Manager",
    "company": "Coworking Studio",
    "description": "Lead member experience at our Berlin location.",
    "job_type": "fulltime",
    "city_name": "Berlin",
    "tags": "community,events,hospitality",
    "external_url": "https://example.com/careers/cm",
    "apply_url": "https://example.com/careers/cm/apply",
    "is_remote": 0,
    "currency": "EUR",
    "sal_min": 35000,
    "sal_max": 45000
  }'
Response — 201 Created
{
  "success": true,
  "status": 201,
  "data": {
    "job_id": 567,
    "page_url": "community-manager-567",
    "hash_identifier": "a1b2c3d4e5f6...",
    "status": "active",
    "action": "created",
    "dedupe_hash": "f7a8b9...",
    "is_paid": true,
    "city": { "id": 42, "name": "Berlin" },
    "apply_url": null,
    "plan_price": 0,
    "expiration_days": 30,
    "message": "Job created and published successfully.",
    "view_url": "https://www.coworkies.com/jobs/community-manager-567/details",
    "payment_url": null,
    "edit_url": "https://www.coworkies.com/jobs/community-manager-567/edit?job_hash=a1b2c3...",
    "unpublish_url": "https://www.coworkies.com/jobs/community-manager-567/unpublish?job_hash=a1b2c3..."
  }
}
Agent prompt — Create Job
Implement a function to create a job posting via the Coworkies API.

POST https://api.coworkies.com/v2/jobs
Headers: X-API-Key: your_api_key, Content-Type: application/json

Required fields:
- title: string (job title)
- company: string (company name)
- description: string (supports HTML: p, br, ul, ol, li, h2, h3, strong, em, a)
- job_type: "fulltime" | "parttime" | "freelance" | "internship"
- tags: string (comma-separated, auto-normalized: trimmed, lowercased, deduplicated)
- City: provide EITHER id_city (int) OR city_name (string, resolved server-side via exact then prefix match)

Optional fields:
- external_url: string (source URL, gets ?ref=coworkiesjobs appended)
- apply_url: string (direct application link for candidates)
- is_remote: 0 | 1 (default 0)
- inv_email: string (valid email for notifications)
- currency: "EUR" | "USD" | "GBP" (default EUR)
- sal_min: int (annual salary min)
- sal_max: int (must be >= sal_min)
- id_space: int (coworking space, must exist and be confirmed)
- id_plan: int (default 5 = free plan)

Deduplication: API computes SHA-256 hash of (title + company + city_id + external_url). If match found, existing job is UPDATED (response action="updated"), otherwise new job is CREATED (action="created").

Requires scope: jobs:write or write.
Job is auto-assigned to your API key's space. Status depends on your plan.

Success response (201):
{ success: true, data: { job_id: int, page_url: string, hash_identifier: string, status: "active"|"draft", action: "created"|"updated", dedupe_hash: string, is_paid: bool, city: { id: int, name: string }, apply_url: string|null, plan_price: int, expiration_days: int, message: string, view_url: string|null, payment_url: string|null, edit_url: string, unpublish_url: string } }

Error response (422):
{ success: false, error: { code: "validation_error", message: "Validation failed", details: ["error 1", "error 2"] } }

Cities

List Cities

GET /v2/cities

Returns confirmed cities. Use search for autocomplete when resolving id_city for job creation.

Query Parameters

ParamTypeDescription
qstringExact match on city slug
searchstringPartial name match (e.g. berl)
Request
curl "https://api.coworkies.com/v2/cities?search=berlin" \
  -H "X-API-Key: your_api_key"
Response
{
  "success": true,
  "status": 200,
  "data": [
    {
      "id_city": "42",
      "city_name": "Berlin",
      "city_slug": "berlin",
      "latitude": "52.5200",
      "longitude": "13.4050"
    }
  ]
}

Spaces

List Spaces

GET /v2/spaces

Returns confirmed coworking spaces. Filter by city slug.

Query Parameters

ParamTypeDescription
qstringFilter by city slug (e.g. berlin)
Request
curl "https://api.coworkies.com/v2/spaces?q=berlin" \
  -H "X-API-Key: your_api_key"
Response
{
  "success": true,
  "data": [
    {
      "space_name": "Example Coworking",
      "space_url": "example-coworking",
      "space_address": "Sample Street 12",
      "latitude": "52.5200",
      "longitude": "13.4050",
      "city_name": "Berlin"
    }
  ]
}

City Spaces

GET /v2/city_spaces

Returns all confirmed spaces with city metadata and LinkedIn URLs. Ideal for directory-style views.

Blog

List Blog Posts

GET /v2/blog

Returns blog posts with category/tag filtering and pagination.

Query Parameters

ParamTypeDescription
categorystringFilter by category
tagstringFilter by tag
limitintResults per page, max 50 (default: 10)
offsetintSkip N results (default: 0)
slugstringFetch single post by slug
Request
curl "https://api.coworkies.com/v2/blog?category=careers&limit=5" \
  -H "X-API-Key: your_api_key"

Get Blog Post

GET /v2/blog?slug={slug}

Returns a single blog post by its slug URL.

Response
{
  "success": true,
  "data": {
    "title": "Launching The Coworking Salary Survey 2026",
    "slug_url": "launching-the-coworking-salary-survey-6",
    "categories": ["careers"],
    "tags": ["coworking-career", "coworking-jobs"],
    "date": "2026-03-07T13:29:51Z",
    "authors": [{
      "name": "Pauline Roussel",
      "username": "pauline"
    }],
    "reading_time": 5
  }
}

Health Check

GET /v2/health

Returns API status, PHP version, memory usage, and Redis connectivity. Useful for monitoring.

Response
{
  "success": true,
  "data": {
    "status": "ok",
    "php_version": "8.4.0",
    "memory_usage": 2097152,
    "redis": {
      "status": "connected",
      "version": "7.0.4"
    }
  }
}

Feedback

POST /v2/feedback

Submit feedback (no authentication required). Accepts JSON or form-encoded body.

Body Parameters

FieldTypeDescription
message requiredstringFeedback message
email optionalstringContact email
url optionalstringPage URL for context
category optionalstringFeedback category

Quick Start

Get up and running in under 2 minutes.

1. Get an API key

Email [email protected] to request access.

2. Make your first request

Fetch active job listings to verify your key works.

3. Post a job

Use city_name to skip the city ID lookup — the API resolves it automatically.

4. Integrate in your app

Use the response data to build your integration.

Step 2 — List jobs
curl "https://api.coworkies.com/v2/jobs?per_page=5" \
  -H "X-API-Key: your_api_key"
Step 3 — Post a job
curl https://api.coworkies.com/v2/jobs \
  -X POST \
  -H "X-API-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Frontend Developer",
    "company": "Remote Co",
    "description": "Build beautiful interfaces.",
    "job_type": "fulltime",
    "city_name": "Lisbon",
    "tags": "react,typescript,remote",
    "is_remote": 1
  }'
Step 4 — App integration
async function postJob(jobData) {
  const response = await fetch(
    'https://api.coworkies.com/v2/jobs',
    {
      method: 'POST',
      headers: {
        'X-API-Key': 'your_api_key',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(jobData),
    }
  );

  const result = await response.json();

  if (!result.success) {
    throw new Error(result.error.message);
  }

  return result.data;
}
<?php
function postJob(array $jobData): array
{
    $ch = curl_init('https://api.coworkies.com/v2/jobs');

    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => json_encode($jobData),
        CURLOPT_HTTPHEADER => [
            'X-API-Key: your_api_key',
            'Content-Type: application/json',
        ],
    ]);

    $response = curl_exec($ch);
    curl_close($ch);

    $result = json_decode($response, true);

    if (!$result['success']) {
        throw new \RuntimeException($result['error']['message']);
    }

    return $result['data'];
}
import requests

def post_job(job_data: dict) -> dict:
    response = requests.post(
        'https://api.coworkies.com/v2/jobs',
        headers={
            'X-API-Key': 'your_api_key',
            'Content-Type': 'application/json',
        },
        json=job_data
    )

    result = response.json()

    if not result['success']:
        raise Exception(result['error']['message'])

    return result['data']