Agency Import API
Push listings into Seeki via API.
Bulk-import real-estate listings from your CRM or property management system. One endpoint, per-item status, automatic TTL.
Get an API key
Access to the Agency Import API requires an active Seeki agency subscription. Each subscription comes with one API key scoped to your agency account.
Subscribe on the For Agents page to get started.
Authentication
Every request must include your API key as a Bearer token in the Authorization header.
Authorization: Bearer seeki_live_your_key_hereKeys starting with seeki_live_ are production keys. Staging keys start with seeki_test_. They are not interchangeable across environments.
Your first import
Send a POST request to /v1/listings with a JSON body containing a listings array. Each element represents one property.
curl -X POST https://ingest.seeki.eu/v1/listings \
-H "Authorization: Bearer seeki_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"listings": [
{
"external_id": "crm-listing-00123",
"listing_type": "SELL",
"real_estate_type": "APARTMENT",
"price": 320000,
"currency": "EUR",
"address": {
"street": "Wenceslas Square 1",
"city": "Prague",
"country_code": "CZ",
"postal_code": "11000"
},
"name": "Sunny 2-bedroom apartment, Wenceslas Square",
"floorage": 65
}
]
}'A successful batch returns HTTP 200 with accepted and rejected arrays:
{
"accepted": [
{ "external_id": "crm-listing-00123", "import_item_id": "uuid-…" }
],
"rejected": []
}Request shape
The top-level body is { listings: [...] }. Required fields per listing:
| Field | Required? | Notes |
|---|---|---|
| external_id | Required | Your CRM's unique ID. Used for idempotent re-imports and deduplication. |
| listing_type | Required | SELL or RENT. |
| real_estate_type | Required | APARTMENT, HOUSE, LAND, COMMERCIAL, or OTHER. |
| price | Required | Numeric. Use currency field for the ISO 4217 code (defaults to EUR). |
| address OR coordinates | Required | Provide either an address object (street, city, country_code) or coordinates ({ lat, lng }). At least one must be present. |
| currency | Optional | ISO 4217 code, e.g. EUR, CZK, PLN. Defaults to EUR. |
| name, description | Optional | Listing title and free-text description. |
| floorage, floor, rooms | Optional | Property size in m², floor number, and room count. |
| images | Optional | Array of public image URLs. The import worker fetches and caches them. |
| contact | Optional | Agent name, email, and phone for inquiry routing. |
See the full schema with all optional fields in the interactive API reference.
Response shape & errors
The /v1/listings endpoint always returns HTTP 200 OK as long as the request itself is well-formed (valid JSON, correct auth header, within size limits). Individual listing failures are reported inside the rejected array — not as HTTP error codes.
Each rejected item includes external_id, error_code, and a human-readable message. Batch-level errors (bad auth, oversized payload) return a non-200 HTTP status with a top-level error_code.
Error codes
Batch-level codes return HTTP errors. Per-item codes appear in the rejected array of a 200 response.
| error_code | HTTP / scope | Meaning |
|---|---|---|
| MISSING_AUTHORIZATION | 401 | No Authorization header was sent. |
| INVALID_KEY | 401 | The key was not found, has been revoked, or has expired. |
| SUBSCRIPTION_INACTIVE | 403 | The agency's subscription is not active or past_due. |
| PAYLOAD_TOO_LARGE | 413 | Request body exceeds the 25 MB limit. |
| TOO_MANY_ITEMS | 413 | The listings array contains more than 5,000 items. |
| VALIDATION_FAILED | per-item | One or more required fields are missing or have an invalid value. Check the message field for details. |
| GEOCODE_ADDRESS_NOT_FOUND | per-item | The provided address could not be resolved to coordinates. Verify street, city, and country_code. |
| GEOCODE_COORDINATES_INVALID | per-item | The provided lat/lng are outside valid ranges (lat ±90, lng ±180). |
| INFRA_QUEUE_REJECTED | per-item | The workflow queue rejected the item, usually due to a transient overload. Retry after a short delay. |
| INFRA_LOST | per-item | The workflow was accepted but its result was never recorded. Use the retry endpoint. |
| INTERNAL_ERROR | per-item | An unexpected server error occurred. Retrying may succeed; contact support if it persists. |
Idempotency / re-imports
Re-posting a listing with the same external_id updates the existing record — it does not create a duplicate. The listing's TTL is refreshed on every successful import.
Internally, each import item maps to a Cloudflare Workflow with the ID:
agency-{agency_id}-{external_id}Listing expiration
Every imported listing has a TTL of 60 days from the most recent successful import. Once the TTL expires, the listing is automatically unpublished by a daily cron job.
To keep a listing live indefinitely, re-import it (even with identical data) before the 60-day window closes. Implementing a nightly re-sync of your active listings is the recommended pattern.
Status + retry
Use GET /v1/import-items/{import_item_id}/status to poll the processing state of a single item. The import_item_id is returned in the accepted array of the original POST response.
curl https://ingest.seeki.eu/v1/import-items/{import_item_id}/status \
-H "Authorization: Bearer seeki_live_your_key_here"{
"import_item_id": "uuid-…",
"external_id": "crm-listing-00123",
"status": "completed",
"listing_id": "uuid-listing"
}If an item ends up in an INFRA_LOST or INFRA_QUEUE_REJECTED state, re-queue it without resubmitting the full batch:
curl -X POST https://ingest.seeki.eu/v1/import-items/{import_item_id}/retry \
-H "Authorization: Bearer seeki_live_your_key_here"Limits
- Body size: 25 MB maximum per request.
- Items per request: 5,000 maximum. Split larger batches.
- No QPS limit is enforced today. Very large bursts may be rate-limited automatically; spread high-volume syncs over multiple requests.
Going live
There are two separate environments. API keys are environment-scoped — a production key will not work on staging, and vice versa.
| Environment | Base URL |
|---|---|
| Staging | https://ingest.seeki.store/v1 |
| Production | https://ingest.seeki.eu/v1 |
All endpoints (/listings, /import-items/{id}/status, /import-items/{id}/retry) are identical across both environments.
Ready to integrate?
Open the interactive reference to explore all fields, try requests in the browser, and download the JSON Schema for your validator.