---
title: "Docs & API reference"
description: "> Quickstart, authentication, plans, error codes, rate-limit headers and the full endpoint list. Machine-readable spec: https://uae.data.evoost.ai/openapi.json"
canonical: "https://uae.data.evoost.ai/docs"
last_updated: "2026-10-25"
---

# UAE Data API documentation

> Quickstart, authentication, plans, error codes, rate-limit headers and the full endpoint list. Machine-readable spec: https://uae.data.evoost.ai/openapi.json

## Quickstart

1. Create a free account at https://uae.data.evoost.ai/auth and open **API keys**.
2. Create a key — the secret is shown once.
3. Call any endpoint at `https://uae-data-api.evoost-ai.workers.dev`.

```bash
curl "https://uae-data-api.evoost-ai.workers.dev/v1/listings/search?emirate=Dubai&area=Dubai%20Marina&transaction_type=sale&min_beds=2&max_beds=2" \
  -H "x-api-key: YOUR_API_KEY"
```

```typescript
const res = await fetch(
  "https://uae-data-api.evoost-ai.workers.dev/v1/analytics/yields?emirate=Dubai&min_confidence=medium",
  { headers: { "x-api-key": process.env.UAE_DATA_API_KEY! } }
);
const { segments } = await res.json();
console.log(segments[0].area, segments[0].gross_yield_pct);
```

## Authentication

Send your key in the `x-api-key` header on every request. Keep keys server-side; rotate or revoke them from the dashboard if exposed — revocation takes effect immediately. Agents using MCP authenticate with OAuth instead: see https://uae.data.evoost.ai/auth.md.

## Plans and limits

Each key inherits its account plan. Plans gate endpoints (403 `plan_upgrade_required` with `required_plan`), set a per-minute rate limit and a monthly quota. Full matrix: https://uae.data.evoost.ai/pricing.md

## Error codes

Errors are JSON: `{ "error": "<code>", "message": "..." }`.

| Status | Code | Meaning |
| --- | --- | --- |
| 400 | `invalid_payload` | Bad query or body parameters. |
| 401 | `unauthorized` | Missing or invalid x-api-key. |
| 403 | `plan_upgrade_required` | Endpoint needs a higher plan; see required_plan. |
| 404 | `not_found` | Resource or route does not exist. |
| 429 | `rate_limited` | Per-minute limit hit; honour retry-after. |
| 429 | `quota_exceeded` | Monthly quota exhausted; upgrade or wait for reset. |

## Response headers

| Header | Meaning |
| --- | --- |
| `x-request-id` | Unique id for support and tracing |
| `x-plan` | Plan of the calling key |
| `RateLimit-Policy` | IETF rate-limit policy: `"minute";q=<limit>;w=60` |
| `RateLimit` | IETF remaining budget: `"minute";r=<remaining>;t=<seconds>` |
| `x-ratelimit-limit` | Requests allowed per minute |
| `x-ratelimit-remaining` | Requests left in the current minute |
| `x-quota-limit` | Monthly quota (absent when unlimited) |
| `x-quota-remaining` | Requests left this month |
| `retry-after` | Seconds to wait after a 429 |
| `idempotent-replayed` | `true` when a /v1/platform POST was replayed from its Idempotency-Key |
| `content-disposition` | Download filename on CSV exports |

## Conventions

### Batch listing lookups

POST /v1/listings/batch with a JSON body { "ids": [...] } (1-50 listing ids, duplicates collapsed) returns the found listings in request order plus a missing array. It counts as one request against your quota, needs the same plan as single listing detail (every plan), and never includes agent phone or email.

### CSV export

Add format=csv (or send Accept: text/csv) to /v1/listings/search, /v1/analytics/yields, /v1/analytics/areas, /v1/analytics/price-cuts and /v1/offplan to get the same rows as RFC 4180 CSV with a Content-Disposition filename. Filters and limits work as for JSON; nested fields become dot-separated columns. CSV export needs the Pro plan or higher (403 plan_upgrade_required otherwise) and is metered like any other request.

### Idempotency-Key

Mutating POSTs under /v1/platform/* (accounts, keys, key rotation, checkout and billing-portal sessions) accept an Idempotency-Key header (1-255 printable ASCII). A retry with the same key and body within 24 hours replays the stored response with idempotent-replayed: true; reusing a key with a different request returns 422 idempotency_key_reused, and a duplicate still in flight returns 409 conflict. Read-only endpoints, including the listings batch, do not need it.

### Rate-limit headers

Every authenticated /v1 response, errors included, carries the IETF RateLimit-Policy ("minute";q=<per-minute limit>;w=60) and RateLimit ("minute";r=<remaining>;t=<seconds to refill>) headers alongside x-ratelimit-limit and x-ratelimit-remaining. A 429 adds retry-after. Public /v1/health and /v1/plans are limited per IP.

### Versioning

/v1 only changes additively; breaking changes ship under a new major path. Deprecations are announced at least 90 days ahead with deprecated: true in the OpenAPI spec and Deprecation, Sunset and Link rel="deprecation" response headers. The full policy is at https://uae-data-api.evoost-ai.workers.dev/policies/versioning.

## Endpoints

- `GET /v1/health` — Health check (`getHealth`)
- `GET /v1/analytics/overview` — Dataset overview (quantitative + qualitative) (`getMarketOverview`)
- `GET /v1/quality/report` — Per-source data-quality output (`getQualityReport`)
- `GET /v1/analytics/price-trends` — Price-movement signal (`getPriceTrends`)
- `GET /v1/analytics/market` — Market velocity (`getMarketStats`)
- `GET /v1/analytics/yields` — Gross rental yields by area (`getYields`)
- `GET /v1/analytics/price-cuts` — Price-cut and days-on-market analytics by area (`getPriceCuts`)
- `GET /v1/analytics/opportunities` — Investment opportunities (`getOpportunities`)
- `GET /v1/analytics/benchmark` — Price benchmark / comps (`getBenchmark`)
- `GET /v1/analytics/areas` — Area leaderboard (`getAreaLeaderboard`)
- `GET /v1/analytics/forecast` — Market forecast (`getForecast`)
- `GET /v1/offplan` — Off-plan / new-development projects (`listOffplanProjects`)
- `GET /v1/listings/{canonical_id}/agents` — Multi-agent view (`getListingAgents`)
- `GET /v1/listings/search` — Search listings (`searchListings`)
- `GET /v1/facets` — Filter facets with counts and slider ranges (cached 1h) (`getFacets`)
- `POST /v1/listings/batch` — Get up to 50 listings in one request (`getListingsBatch`)
- `GET /v1/listings/{canonical_id}` — Get listing detail (`getListing`)
- `GET /v1/listings/{canonical_id}/history` — Listing history (`getListingHistory`)
- `GET /v1/map/points` — Map points for the caller's plan (`getMapPoints`)
- `GET /v1/map/showcase` — Anonymized landing-page sample (`getMapShowcase`)
- `GET /v1/areas` — Areas metadata (`listAreas`)
- `GET /v1/buildings` — Buildings metadata (`listBuildings`)
- `POST /v1/admin/sources` — Upsert source configuration (`upsertSource`)
- `GET /v1/plans` — Public plan catalog (pricing page) (`listPlans`)
- `GET /v1/me` — Current caller identity, plan, key and month-to-date usage (`getMe`)
- `GET /v1/usage` — Daily request series for the caller's account (not metered) (`getUsage`)
- `GET /v1/registry/developers` — Official DLD developer registry (current-year registrations) (`listDldDevelopers`)
- `GET /v1/registry/areas` — Official DLD area lookup (`listDldAreas`)
- `POST /v1/platform/accounts` — Create or update a customer account (idempotent upsert) (`upsertAccount`)
- `GET /v1/platform/accounts/{owner_id}` — Account with plan, subscription, usage and active keys (`getAccount`)
- `PATCH /v1/platform/accounts/{owner_id}` — Manual plan override (admin only; e.g. enterprise contracts) (`overrideAccountPlan`)
- `GET /v1/platform/accounts/{owner_id}/usage` — Daily usage series for an account (`getAccountUsage`)
- `GET /v1/platform/accounts/{owner_id}/keys` — List the account's API keys (never returns secrets) (`listAccountKeys`)
- `POST /v1/platform/accounts/{owner_id}/keys` — Issue an API key for the account (secret returned once) (`createAccountKey`)
- `GET /v1/platform/keys` — List all non-admin keys (admin only) (`listKeys`)
- `POST /v1/platform/keys` — Issue an unowned key, e.g. a platform key (admin only) (`createUnownedKey`)
- `PATCH /v1/platform/keys/{id}` — Rename a key (admins may also set plan) (`updateKey`)
- `DELETE /v1/platform/keys/{id}` — Revoke a key (effective immediately) (`revokeKey`)
- `POST /v1/platform/keys/{id}/rotate` — Rotate a key (new secret returned once; old key revoked) (`rotateKey`)
- `GET /v1/platform/keys/{id}/usage` — Usage series for the key's account (`getKeyUsage`)
- `PUT /v1/platform/accounts/{owner_id}/subscription` — Push subscription state from the web app's payment provider (BILLING_PROVIDER=lovable) (`syncAccountSubscription`)
- `POST /v1/platform/accounts/{owner_id}/checkout` — Create a Stripe Checkout Session for a paid plan (`createCheckoutSession`)
- `POST /v1/platform/accounts/{owner_id}/portal` — Create a Stripe Customer Portal session (upgrade, downgrade, cancel, invoices, payment method) (`createPortalSession`)
- `GET /v1/platform/accounts/{owner_id}/invoices` — Latest 24 Stripe invoices for the account (`listAccountInvoices`)
- `GET /policies/versioning` — API versioning and deprecation policy (`getVersioningPolicy`)
- `POST /webhooks/stripe` — Stripe webhook (signature-verified; not for API clients) (`stripeWebhook`)

## MCP server

Connect MCP clients to https://uae.data.evoost.ai/mcp (Streamable HTTP, OAuth sign-in). Tools: `search_listings`, `get_listing`, `get_listings`, `benchmark_price`, `find_opportunities`, `market_yields`, `area_leaderboard`, `price_forecast`, `offplan_projects`, `price_cuts`, `get_account`.

## Documentation MCP server

Documentation MCP server (public, read-only, no sign-in): https://uae.data.evoost.ai/docs/mcp (Streamable HTTP; tools `search_docs`, `get_doc`; server card at https://uae.data.evoost.ai/docs/mcp/server-card). It searches and returns these docs only, no market data.

## Testing / sandbox

- There is no separate sandbox host or test API: agents test against production, which is safe because the data API is read-only. Every /v1 data endpoint only reads (GET requests, plus POST /v1/listings/batch for multi-id lookups), so no data call can change production data.
- Use a Free plan key to test: it costs nothing, needs no card and covers search, listing detail and the DLD registry (1,000 requests per month). API keys created in the dashboard carry only the read scope, so they cannot call the account, key and billing endpoints under /v1/platform/*.
- The documentation MCP server is public and read-only, so it is a safe first connection for an agent.
- Billing: the published site takes real payments through Paddle. Paddle's test mode (test cards, with a banner saying payments are in test mode) is only used on non-production preview deployments of the web app, and test-mode purchases are not a supported way to get a paid plan on production.