Map Export API for Developers: Integrating osm2cdr.ru

2026-02-124 min read
APIRESTpollingintegration

osm2cdr.ru provides a REST API for automating OpenStreetMap map exports. Instead of manual work through the interface — programmatic access to 127 export formats, 165 coordinate systems, 4 cartographic styles and 85 colour palettes.

Quick Start

Getting an API Key

Register on osm2cdr.ru and get your API key from the dashboard. The key is passed in the X-API-Key header.

Access and payment:

What Terms
API requests Free
Full file of any format Paid — per file or from the wallet
Demo file Free
Order price GET /api/pricing/quote or the /pricing/ page
Technical area limits GET /api/format-limits

First Request (Python)

import requests
import time

API_KEY = "your-api-key"
BASE_URL = "https://osm2cdr.ru/api"

# Step 1: Create export task
response = requests.post(f"{BASE_URL}/export", json={
    "bbox": [37.60, 55.74, 37.64, 55.76],  # [min_lon, min_lat, max_lon, max_lat]
    "format": "geojson",
    "style": "default",
    "detail_level": 4
}, headers={"X-API-Key": API_KEY})

task = response.json()
task_id = task["task_id"]
print(f"Task created: {task_id}")

# Step 2: Wait for completion
while True:
    status = requests.get(
        f"{BASE_URL}/status/{task_id}",
        headers={"X-API-Key": API_KEY}
    ).json()

    if status["status"] == "completed":
        break
    elif status["status"] == "failed":
        print(f"Error: {status['error']}")
        break

    print(f"Progress: {status.get('progress', 0)}%")
    time.sleep(2)

# Step 3: Download result
download_url = status["download_url"]
result = requests.get(download_url)
with open("moscow_center.geojson", "wb") as f:
    f.write(result.content)
print("Downloaded!")

First Request (JavaScript)

const API_KEY = 'your-api-key';
const BASE_URL = 'https://osm2cdr.ru/api';

async function exportMap() {
  // Create task
  const response = await fetch(`${BASE_URL}/export`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': API_KEY
    },
    body: JSON.stringify({
      bbox: [37.60, 55.74, 37.64, 55.76],
      format: 'svg',
      style: 'blueprint'
    })
  });

  const { task_id } = await response.json();

  // Wait via REST polling (every 2 seconds)
  while (true) {
    const statusResp = await fetch(`${BASE_URL}/status/${task_id}`, {
      headers: { 'X-API-Key': API_KEY }
    });
    const data = await statusResp.json();
    console.log(`Progress: ${data.progress || 0}% — ${data.step || ''}`);

    if (data.status === 'completed') {
      window.location.href = data.download_url;
      return;
    }
    if (data.status === 'failed') {
      throw new Error(data.error);
    }
    await new Promise((r) => setTimeout(r, 2000));
  }
}

API Endpoints

POST /api/render

Create an export task.

Parameters: - bbox (array) — bounding box [min_lon, min_lat, max_lon, max_lat]. - polygon (GeoJSON) — arbitrary polygon instead of bbox. - format (string) — export format (geojson, svg, pdf, dwg, stl, etc.). - style (string) — map style (default, blueprint, vintage, neon, satellite, etc.). - detail_level (int, 1-6) — detail level (1=minimum, 6=maximum). - crs (string) — coordinate system (EPSG:4326, EPSG:32637, msk-77, etc.). - layers (array) — which layers to include (roads, buildings, water, green, etc.).

GET /api/status/{task_id}

Task status.

Response:

{
  "task_id": "abc123",
  "status": "processing",
  "progress": 65,
  "created_at": "2026-03-15T10:00:00Z",
  "format": "geojson",
  "bbox": [37.60, 55.74, 37.64, 55.76]
}

Statuses: queued -> processing -> completed / failed.

Task progress (polling)

The current API uses REST polling via GET /api/status/{task_id} to track progress. The WebSocket endpoint /ws/task/{task_id} exists only as a stub (it immediately closes with code 1001 "Not implemented. Poll /api/status/{task_id}") and is reserved for a future Celery -> Redis Pub/Sub bridge — do not use it in production code.

Recommended interval is 2-5 seconds. The /api/status/{task_id} response contains status, progress (0-100), step (current stage), error, and a files field with download URLs on completion:

{"status": "processing", "progress": 45, "step": "rendering"}
{"status": "completed", "progress": 100, "files": {"geojson": "..."}}

GET /api/formats

List of available formats with metadata.

GET /api/styles

List of available map styles.

Geo-Analytics API

In addition to export, the API provides territory analytics:

# Scoring for a point
scoring = requests.get(f"{BASE_URL}/scoring", params={
    "lat": 55.7558,
    "lon": 37.6176
}, headers={"X-API-Key": API_KEY}).json()

print(f"Walk Score: {scoring['walk_score']}")
print(f"Green Score: {scoring['green_score']}")
print(f"Livability: {scoring['livability_index']}")

# Area statistics
stats = requests.get(f"{BASE_URL}/area-stats", params={
    "bbox": "37.60,55.74,37.64,55.76"
}, headers={"X-API-Key": API_KEY}).json()

print(f"Buildings: {stats['buildings_count']}")
print(f"Roads: {stats['roads_total_km']} km")
print(f"Green area: {stats['green_area_km2']} km2")

Error Handling

The API returns standard HTTP codes:

Code Reason Action
400 Invalid bbox or format; BBOX_TOO_LARGE — area exceeds the technical limit Check parameters; on BBOX_TOO_LARGE reduce the bbox
401 Invalid API key Check X-API-Key
402 Payment or login required (no_credits / login_required) Pay for the file or top up the wallet
429 Rate limit exceeded Wait, retry after Retry-After
500 Server error Retry after 30 seconds

Recommendation: use exponential backoff for retry logic.

Integrate map export into your applications via the osm2cdr.ru API. Documentation and examples are available on the site at /docs/.

← All articles