# JustIdea Agency public API

> Read-only REST API for the public website of JustIdea Agency (justidea.agency), a Polish e-commerce and performance marketing agency from Kraków. Search and read pages, get the published starting prices of services and the contact options. The same data is available over MCP at https://justidea.agency/mcp.

- Base URL: https://justidea.agency/api/v1
- OpenAPI 3.1: https://justidea.agency/api/v1/openapi (JSON), https://justidea.agency/api/openapi.yaml (YAML)
- API catalog (RFC 9727): https://justidea.agency/.well-known/api-catalog
- MCP server: https://justidea.agency/mcp, listed in the official MCP registry: https://registry.modelcontextprotocol.io/v0/servers/agency.justidea%2Fjustidea-agency/versions/latest
- Agent authentication: https://justidea.agency/auth.md
- Version: 1.0.0

The API never submits forms, books meetings or sends messages. To get in touch, give the user the link, e-mail address or phone number from `/api/v1/contact`. All prices are net starting prices (VAT excluded) as published on the website; the final quote depends on scope.

## Endpoints

| Method | Path | What it returns |
|---|---|---|
| GET | /api/v1 | API description with every endpoint URL |
| GET | /api/v1/health | Catalog status: number of pages |
| GET | /api/v1/search?q=&lang=&type=&limit= | Pages matching a query (limit 1-25, default 8) |
| GET | /api/v1/pages?type=&lang=&limit=&cursor= | Pages with cursor pagination (limit 1-100, default 50) |
| GET | /api/v1/page?url= | One page as Markdown |
| GET | /api/v1/services?lang= | Services with published starting prices: pl (PLN), en (EUR) |
| GET | /api/v1/contact?lang= | E-mail, phone, contact page (pl, en) |
| GET | /api/v1/identity | The agent behind the bearer token (token required) |
| POST | /api/v1/batch | Up to 10 GET operations in one request |

`lang` is one of pl, en. `type` is one of services_index, service_area, service, city_page, pricing, case_studies, event, blog, company. Paths work with or without a trailing slash. Every GET also answers HEAD.

Example:

```bash
curl "https://justidea.agency/api/v1/search?q=prestashop+store+price&lang=en"
curl "https://justidea.agency/api/v1/services?lang=en"
```

## Authentication

None required. An optional OAuth 2.1 bearer token (scope `site:read`) identifies the agent and raises the rate limit; how to get one is in https://justidea.agency/auth.md. Protected resource metadata (RFC 9728): https://justidea.agency/.well-known/oauth-protected-resource. An invalid or expired token gets `401` with `WWW-Authenticate: Bearer resource_metadata="..."`.

## Rate limits

120 requests per 60 seconds per IP address, 600 per agent with a token. Every API response carries `RateLimit-Policy` (for example `"ip";q=120;w=60`) and `X-RateLimit-Limit`. A `429` carries `Retry-After` in seconds and `RateLimit: "ip";r=0;t=60`. A batch counts as one request. This file and the OpenAPI document are not rate-limited.

## Pagination

`/api/v1/pages` is cursor-based. The response has `data`, `total`, `limit` and `next_cursor`; pass `next_cursor` as `cursor` until it is `null`. The next page URL is also in the `Link: <...>; rel="next"` header. Cursors are opaque: do not build them yourself.

## Batch and retries

`POST /api/v1/batch` with `{"requests": [{"id": "a", "path": "/api/v1/search?q=seo"}, {"id": "b", "path": "/api/v1/services?lang=en"}]}` returns `{"responses": [{"id": "a", "status": 200, "body": {...}}, ...]}` in the same order. Up to 10 operations, GET only, not on batch, identity or the API documents.

Every endpoint, the batch included, is read-only and has no side effects, so any request is safe to retry. The batch accepts an optional `Idempotency-Key` header (1 to 255 visible ASCII characters) and sends it back.

## Versioning and deprecation

The major version is in the path: `/api/v1`. Within v1 changes are additive only: new endpoints, new optional parameters, new response fields; ignore fields you do not know. A breaking change ships as `/api/v2`, and v1 keeps working. From the day v1 (or an endpoint) is deprecated its responses carry a `Deprecation` header (RFC 9745); once a removal date is set they also carry `Sunset` (RFC 8594) with that date, at least 90 days later. `info.version` in the OpenAPI document follows SemVer. No endpoint is deprecated today.

## Errors

Every 4xx and 5xx response is `application/problem+json` (RFC 9457):

```json
{"type": "https://justidea.agency/api.md#not-found", "title": "Not found", "status": 404, "detail": "No public page at ...", "instance": "/api/v1/page", "code": "not-found"}
```

`code` is stable and machine-readable; `type` links to its section below.

### invalid-parameter

HTTP 400. A query parameter (or the Idempotency-Key header) is missing, too long, not in the allowed list or out of range. `detail` names it. Fix the request; retrying it unchanged will fail again.

### invalid-body

HTTP 400. The batch body is not `{"requests": [...]}` with 1 to 10 operations, or an operation is not a GET on `/api/v1`.

### unsupported-media-type

HTTP 415. The batch body must be sent with `Content-Type: application/json`.

### payload-too-large

HTTP 413. The batch body is over 16000 characters.

### unauthorized

HTTP 401. `/api/v1/identity` and `/agent/identity` need a bearer token. `WWW-Authenticate` points at the protected resource metadata; how to get a token is in https://justidea.agency/auth.md. Every other endpoint works without a token.

### invalid-token

HTTP 401. The bearer token is invalid or expired. Get a new one, or call without a token (anonymous access works everywhere except identity).

### not-found

HTTP 404. No public page at the given URL, or no such endpoint. Find pages with `/api/v1/search` or `/api/v1/pages`.

### method-not-allowed

HTTP 405. The API is read-only: GET (and HEAD) everywhere, POST only on `/api/v1/batch`. The `Allow` header lists what works.

### rate-limited

HTTP 429. Over 120 requests per minute from one IP (600 with a token). Wait the number of seconds in `Retry-After`.

### unavailable

HTTP 503. The site catalog is temporarily unavailable. Wait the number of seconds in `Retry-After`.

### internal-error

HTTP 500. Something failed on our side. Retry later; if it persists, write to contact@justidea.agency.
