{
  "openapi": "3.0.1",
  "info": {
    "title": "OLX Car Listings Scraper - 6 Countries, JSON Output",
    "description": "Scrape car listings from OLX across Romania, Poland, Bulgaria, Portugal, Ukraine, and Kazakhstan. Structured JSON with price, make, model, year, mileage, photos, and seller info. Optional NHTSA vPIC VIN decoding adds make/model/engine/plant for listings that disclose a VIN. No proxy.",
    "version": "1.0",
    "x-build-id": "tGwLzH2nCMx5YyRnm"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/extractify-labs~olx-cars/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-extractify-labs-olx-cars",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/extractify-labs~olx-cars/runs": {
      "post": {
        "operationId": "runs-sync-extractify-labs-olx-cars",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/extractify-labs~olx-cars/run-sync": {
      "post": {
        "operationId": "run-sync-extractify-labs-olx-cars",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "properties": {
          "startUrls": {
            "title": "Start URLs",
            "type": "array",
            "description": "Leave empty (default) to use the structured filters below — that is the right path for most users. Paste OLX car listing or search URLs here only when you already have a fully filtered page on OLX (e.g. a model-specific URL like '.../audi/q5/' or a saved search) and want to paginate it directly. When set, the structured filters below (country, brands, query, yearFrom, yearTo, priceFrom, priceTo, filterByCurrency) are ignored — only maxItems, sortBy, sellerType, excludeDamaged, firstOwnerOnly, and serviceBookOnly still apply. Country is auto-inferred from the URL domain.",
            "items": {
              "type": "object",
              "required": [
                "url"
              ],
              "properties": {
                "url": {
                  "type": "string",
                  "title": "URL of a web page",
                  "format": "uri"
                }
              }
            }
          },
          "country": {
            "title": "Country",
            "enum": [
              "ro",
              "pl",
              "bg",
              "pt",
              "ua",
              "kz"
            ],
            "type": "string",
            "description": "OLX country domain to scrape. Brazil (BR) is not available in v1 — see actor description. Ignored when startUrls is provided.",
            "default": "ro"
          },
          "brands": {
            "title": "Car Brands",
            "type": "array",
            "description": "Car brands to filter by (e.g. BMW, Toyota, Volkswagen). The actor resolves each name to the correct per-country category ID using a bundled brand map — names are case-insensitive. Unrecognised brand names log a warning with a list of available brands for the selected country. Leave empty to scrape all brands. Ignored when startUrls is provided.",
            "items": {
              "type": "string"
            }
          },
          "query": {
            "title": "Keyword Search",
            "type": "string",
            "description": "Free-text keyword search query (maps to the OLX ?query= parameter). Combined with other structured filters. Leave empty to skip keyword filtering. Ignored when startUrls is provided."
          },
          "yearFrom": {
            "title": "Year From",
            "minimum": 1900,
            "maximum": 2099,
            "type": "integer",
            "description": "Minimum manufacture year (inclusive). Maps to filter_float_year:from. Valid range: 1900–2099. Ignored when startUrls is provided."
          },
          "yearTo": {
            "title": "Year To",
            "minimum": 1900,
            "maximum": 2099,
            "type": "integer",
            "description": "Maximum manufacture year (inclusive). Maps to filter_float_year:to. Valid range: 1900–2099. Ignored when startUrls is provided."
          },
          "priceFrom": {
            "title": "Price From",
            "minimum": 0,
            "type": "integer",
            "description": "Minimum price (inclusive) in the selected priceCurrency. Maps to filter_float_price:from. Ignored when startUrls is provided."
          },
          "priceTo": {
            "title": "Price To",
            "minimum": 0,
            "type": "integer",
            "description": "Maximum price (inclusive) in the selected priceCurrency. Maps to filter_float_price:to. Ignored when startUrls is provided."
          },
          "priceCurrency": {
            "title": "Price Currency",
            "enum": [
              "EUR",
              "RON",
              "PLN",
              "UAH",
              "USD",
              "BGN",
              "KZT"
            ],
            "type": "string",
            "description": "Currency for priceFrom/priceTo filters. Must match the listing currency on OLX for the filter to apply. EUR is the most interoperable choice across all supported countries. Ignored when startUrls is provided. When filterByCurrency is true, only listings in this currency are returned.",
            "default": "EUR"
          },
          "filterByCurrency": {
            "title": "Filter by Currency",
            "type": "boolean",
            "description": "When true, drops listings whose currency does not match priceCurrency. Only effective in structured-filter mode (ignored when startUrls is provided). Leave false (default) to return all listings regardless of listed currency — matching behaviour before this option was added.",
            "default": false
          },
          "sellerType": {
            "title": "Seller Type",
            "enum": [
              "any",
              "private",
              "business"
            ],
            "type": "string",
            "description": "Filter listings by seller type. 'Private' returns individual sellers only (owner_type=private); 'Business' returns dealers only (owner_type=business); 'Any' applies no filter. Applies in both structured-filter mode and startUrls mode. Universal across all 6 supported countries.",
            "default": "any"
          },
          "excludeDamaged": {
            "title": "Exclude Damaged",
            "type": "boolean",
            "description": "Drop listings flagged as damaged / requires-repairs. Applied client-side after fetching. Supported on: RO, PL, PT, UA, KZ. Not applicable on BG (no damage flag in BG condition enum) — BG listings pass through unchanged and a one-time INFO log is emitted. When true and the active country lacks the filter, the run proceeds as if the parameter were false.",
            "default": false
          },
          "firstOwnerOnly": {
            "title": "First Owner Only",
            "type": "boolean",
            "description": "Keep only listings with first owner. Applied client-side after fetching. Supported on: BG (technical_condition == 'first-owner'), UA (condition array contains 'first-owner'), KZ (ownersCount == 1). Not applicable on RO, PL, PT (no first-owner field in their OLX params) — listings on these countries pass through unchanged and a one-time INFO log is emitted. When true and the active country lacks the filter, the run proceeds as if the parameter were false.",
            "default": false
          },
          "serviceBookOnly": {
            "title": "Service Book Only",
            "type": "boolean",
            "description": "Keep only listings with a stamped service book. Applied client-side after fetching. Supported on Bulgaria (olx.bg) only — checks for 'service-book' in the listing's raw technical_condition value. Not applicable on RO, PL, PT, UA, KZ (no service-book field in their OLX condition params) — listings on these countries pass through unchanged and a one-time INFO log is emitted. When true and the active country lacks the filter, the run proceeds as if the parameter were false.",
            "default": false
          },
          "sortBy": {
            "title": "Sort Order",
            "enum": [
              "created_at:desc",
              "filter_float_price:asc",
              "filter_float_price:desc",
              "relevance"
            ],
            "type": "string",
            "description": "Sort order for results. Applies in both structured-filter mode and startUrls mode.",
            "default": "created_at:desc"
          },
          "maxItems": {
            "title": "Max Items",
            "minimum": 1,
            "type": "integer",
            "description": "Maximum number of car listings to return. The OLX API caps any single unfiltered query at 1,000 results. When maxItems exceeds 1,000, the actor automatically slices by brand and year range to maximise coverage — this significantly increases run time and compute cost.",
            "default": 1000
          },
          "outputMode": {
            "title": "Output Mode",
            "enum": [
              "full",
              "compact"
            ],
            "type": "string",
            "description": "Controls which output fields are emitted. 'Full' returns all available fields (default). 'Compact' returns a reduced 18-field set (offerId, url, country, title, price, currency, make, model, year, mileageKm, fuelType, transmission, bodyType, condition, description, engineCapacityCm3, powerHp, color) — optimised for LLM/RAG pipelines where token cost matters. descriptionMaxLength applies in both modes.",
            "default": "full"
          },
          "descriptionMaxLength": {
            "title": "Description Max Length",
            "minimum": 0,
            "type": "integer",
            "description": "Maximum character length for the description field. When set, long listing descriptions are truncated to this length (truncation is character-boundary safe). Set to 0 to drop the description field entirely. Leave unset for no truncation (default). Applies in both full and compact output modes."
          },
          "enrichVIN": {
            "title": "Enrich VIN",
            "type": "boolean",
            "description": "When enabled, each listing that carries a valid 17-character VIN is enriched with decoded vehicle data from the free NHTSA vPIC API (make, model, engine specs, plant info, and more). Results are cached cross-run in an Apify KV store so the same VIN is never decoded twice. Most useful for Poland (PL) and Ukraine (UA) where OLX sellers routinely disclose VINs; other countries rarely include a VIN in their listings. vPIC lookup failures are non-fatal — the listing is emitted without the vinDecoded field. Excluded from compact output mode.",
            "default": false
          },
          "incrementalMode": {
            "title": "Incremental Mode",
            "type": "boolean",
            "description": "When enabled, each run compares scraped listings against the previous run's snapshot and labels each item NEW, UPDATED, UNCHANGED, or MISSING. Only new and changed listings are emitted by default — significantly reducing output size and cost for ongoing monitoring. The first run with this enabled builds the baseline; no items are emitted on that run (they are stored silently in the state snapshot). Disable to use standard full-scrape mode.",
            "default": false
          },
          "stateKey": {
            "title": "State Key",
            "type": "string",
            "description": "Name of the Apify key-value store entry used to persist the listings snapshot between runs. Use a unique key per monitoring job — e.g. 'olx-cars-ro-bmw' for Romanian BMWs, 'olx-cars-pt-all' for all Portuguese listings. Changing this key resets the baseline (next run emits everything as NEW). Only used when Incremental Mode is on.",
            "default": "olx-cars-state"
          },
          "emitUnchanged": {
            "title": "Emit Unchanged",
            "type": "boolean",
            "description": "Include listings whose tracked fields (price, currency, condition, mileage, title) are identical to the previous run snapshot (changeType: UNCHANGED). Useful if you need a full dataset export alongside change signals. Leave off to minimise output and cost. Only used when Incremental Mode is on.",
            "default": false
          },
          "emitMissing": {
            "title": "Emit Missing",
            "type": "boolean",
            "description": "Emit listings that were in the previous snapshot but absent from the current results (changeType: MISSING). These are likely sold or removed. On active OLX markets, expect 30–50% of tracked listings to appear as MISSING per day. Enable only when you need deletion tracking. Suppressed automatically when the run is capped by maxItems (to avoid false positives). Only used when Incremental Mode is on.",
            "default": false
          },
          "notifyOn": {
            "title": "Notify On",
            "enum": [
              "none",
              "new_listings",
              "price_drops",
              "both"
            ],
            "type": "string",
            "description": "Controls which events trigger a run-end digest. 'Disabled' produces no digest. 'New listings only' reports listings that appeared since the last run. 'Price drops only' reports listings whose price fell by at least notifyMinPriceDropPct. 'Both' reports new listings and price drops together. Requires Incremental Mode to be enabled — the actor will fail with a clear error if notifyOn is not 'none' and incrementalMode is false.",
            "default": "none"
          },
          "notifyMinPriceDropPct": {
            "title": "Min Price Drop %",
            "minimum": 1,
            "maximum": 99,
            "type": "integer",
            "description": "Minimum price reduction percentage vs the previous snapshot price for a listing to appear in the digest's price drops list. A value of 5 means a listing must have dropped by at least 5% to qualify. Only meaningful when notifyOn includes price drops.",
            "default": 5
          },
          "notifyTopN": {
            "title": "Max Items in Digest",
            "minimum": 1,
            "maximum": 200,
            "type": "integer",
            "description": "Maximum number of items to include in each section (new listings, price drops) of the digest payload. New listings are ranked by most-recent first; price drops are ranked by largest drop first. Each section is capped independently.",
            "default": 20
          },
          "notifyWebhookUrl": {
            "title": "Webhook URL",
            "type": "string",
            "description": "Optional URL to POST the digest JSON to at run end. Supports Slack incoming webhooks, Discord webhooks, Make/n8n/Zapier, and any generic HTTP endpoint. NOTE: direct POST to Telegram Bot API is NOT supported — Telegram expects a bespoke {chat_id, text} body that does not match the digest shape; use Zapier/Make as a relay (see README). Leave this URL empty to disable outbound HTTP — the digest is always written to the Apify key-value store (olx-cars-notifications) regardless. Keep this URL private — it is stored in run input and visible in run history.",
            "default": ""
          },
          "pageLimit": {
            "title": "Page Limit",
            "minimum": 1,
            "maximum": 50,
            "type": "integer",
            "description": "Number of listings to request per OLX API call (the 'limit' parameter). OLX's cars endpoint rejects values above 50 with HTTP 400. Lower values produce finer per-page progress logs at the cost of more requests. Default 50 is the maximum and is unchanged from prior versions. Only affects request volume — not which listings are returned.",
            "default": 50
          },
          "sliceYearStep": {
            "title": "Year Slice Step",
            "minimum": 1,
            "maximum": 50,
            "type": "integer",
            "description": "Width of each year band (in years) when auto-slicing by year for brand/query result sets that exceed 1000 visible listings. Smaller values (e.g. 2) give narrower bands and better coverage for densely-listed years. Larger values (e.g. 10) give fewer API calls. Default 5 covers the 1900–2100 range in 40 bands. Only relevant when maxItems > 1000. No effect when startUrls is provided (band-based slicing is not used in URL-sourced mode).",
            "default": 5
          },
          "slicePriceStep": {
            "title": "Price Slice Step",
            "minimum": 1000,
            "maximum": 500000,
            "type": "integer",
            "description": "Width of each price band (in the same currency as priceCurrency) when auto-slicing by price for year-band result sets that still exceed 1000 visible listings. Smaller values (e.g. 2000) give finer slices; larger values (e.g. 25000) give fewer API calls. Default 5000 gives approximately 100 bands across 0–500k but most above 50k will be empty. Only relevant when maxItems > 1000. No effect when startUrls is provided (band-based slicing is not used in URL-sourced mode).",
            "default": 5000
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}