Blog / API guides

AutoScout24 API: Search European Car Listings as JSON

One GET request to /v1/autoscout24/search returns AutoScout24 car listings as JSON: price, mileage, first registration, power, fuel, location and seller. You filter by make_id, model_id, price, mileage and more, and the taxonomy endpoint tells you which make and model values are valid. Each call costs 1 credit, and failed calls cost nothing.

What does the AutoScout24 search endpoint return?

The search endpoint returns a page of listings for a market, each with a listing_id, the listing url, a title, a nested price, mileage and first_registration, power in kW and PS, fuel, transmission, make, model, location, seller and images. A pagination object follows the listings. It is the call to start with when you want many cars filtered by criteria rather than one known listing.

The AutoScout24 API has six endpoints, all 1 credit per call. Search is the broad one. The others fetch a single listing, a dealer, a dealer's inventory or its reviews, and the taxonomy of valid filter values.

How do you make your first search request?

Send a GET to /v1/autoscout24/search with your key in the X-API-Key header and any filters as query parameters. The call below asks the German market for one make and model priced between 5,000 and 20,000, sorted by price, and takes page 2 at 20 results per page. Get a free API key first if you don't have one; see authentication for the details.

curl "https://api.perceptlayer.com/v1/autoscout24/search?market=de&make_id=13&model_id=1639&price_min=5000&price_max=20000&sort=price&page=2&limit=20" \
  -H "X-API-Key: $API_KEY"

You can also run it from the playground on the AutoScout24 search page, which fills in the same parameters and runs a real call with your key.

What does the recorded response look like?

This is an excerpt of the recorded response for the request above, trimmed with …. The first listing is a 2013 BMW 316 Touring diesel with 215,000 km, listed by a dealer in Mainz-Kastel.

{
  "success": true,
  "data": {
    "listings": [
      {
        "listing_id": "f81b29b2-19e3-4418-8801-03578ed51631",
        "title": "BMW 316 Touring 316d",
        "price": {
          "amount": 5700,
          "currency": "EUR",
          "formatted": "€ 5.700",
          "evaluation": 2,
          "is_evaluation_enabled": true
        },
        "mileage": {
          "value_km": 215000,
          "unit": "km",
          "raw": "215.000 km"
        },
        "first_registration": {
          "raw": "06/2013",
          "year": 2013,
          "month": 6
        },
        "power": {
          "kw": 85,
          "ps": 116
        },
        "fuel": "Diesel",
        "transmission": "Schaltgetriebe",
        "make": "BMW",
        "model": "316",
        "model_group": "3er",
        "variant": "Touring",
        "motor_type": "316d",
        "location": {
          "country_code": "DE",
          "zip": "55252",
          "city": "Mainz-Kastel"
        },
        "seller": {
          "id": "32515898",
          "type": "Dealer",
          "dealer_slug": "rheinkastelautomobile"
        },
        …
      },
      …
    ],
    "pagination": {
      "page": 2,
      "limit": 20,
      "total": 193,
      "total_pages": 10,
      "has_more": true,
      "next_page": 3
    }
  },
  "meta": {
    "duration_ms": 2183,
    "endpoint_family": "search"
  }
}

A few details matter when you parse it. price.amount is a number and price.formatted is the display string, so use the number. mileage.value_km and first_registration.year are numeric too, while raw keeps the original text. The fuel and transmission values come back as text, and in the recorded de response the transmission is German (Schaltgetriebe).

The second listing in the same recording has seller.type set to PrivateSeller and carries no dealer_slug, while the dealer listing does. Check that the key exists before you use it, because dealer_slug is the required input of the dealer endpoints.

Which parameters does the search endpoint take?

Search has 25 optional parameters and no required ones. They cover location (market, zip, radius), the car (make_id, model_id, fuel[], transmission[], body_type[], equipment[], usage_state[]), numeric ranges, the seller, ordering and paging. Since nothing is required, narrow the search with at least a make or a price range before you page through results.

Parameter Type Allowed values or notes
market string de, at, be, it, fr, nl, es, com, pl
make_id, model_id string Up to 12 characters; get valid values from taxonomy
price_min, price_max integer Price range
mileage_min, mileage_max integer Mileage range
registration_year_min, registration_year_max integer First registration year range
power_min, power_max integer Used with power_unit
power_unit string kw, ps
fuel[] list of string 2, 3, B, C, D, E, H, L, M, O
transmission[] list of string A, M, S
body_type[] list of integer 1, 2, 3, 4, 5, 6, 7, 12, 13
equipment[] list of integer Equipment IDs
seller_type string dealer, private
offer string new, used, annual, N, U, J
usage_state[] list of string N, U
zip, radius string, integer Search around a postcode; zip up to 12 characters
sort string standard, price, age, mileage, year, power, make, distance, financerate, leasing_rate
order string asc, desc
page, limit integer Paging

The fuel, body type and equipment codes are opaque. Ask the taxonomy endpoint for them rather than guessing, which is the next section.

How do you find a valid make_id and model_id?

Call /v1/autoscout24/taxonomy with include[] set to the lists you want: makes, models, equipment, body_types, fuels, transmissions, sorting_keys, offers, radii or countries. make_id is also a taxonomy parameter, which is how you ask for the models of one make. Each taxonomy call costs 1 credit.

curl -g "https://api.perceptlayer.com/v1/autoscout24/taxonomy?market=de&include[]=makes&include[]=fuels" \
  -H "X-API-Key: $API_KEY"

The -g flag stops cURL from treating the square brackets as a glob. We don't have a recorded taxonomy response, so we won't show one here. Run it in the taxonomy playground to see the exact shape for your market.

The workflow is two steps. Look up the make in taxonomy and take its ID, call taxonomy again with that make_id and include[]=models to find the model, then pass both IDs to search. The example request above uses make_id=13 and model_id=1639; in the recorded response those IDs resolve to BMW 316 models.

How do you sweep prices for a whole month?

Loop over your make and model pairs, page through each until has_more is false, and write one row per listing with a date. The recorded request had total: 193 and total_pages: 10 at 20 per page, so one pair cost 10 calls. The script below does that and stops on any non-success response, so you never page blindly past an error.

import os
import time
import requests

BASE = "https://api.perceptlayer.com/v1/autoscout24/search"
HEADERS = {"X-API-Key": os.environ["API_KEY"]}


def sweep(make_id, model_id, market="de", limit=20):
    page, rows, credits = 1, [], 0
    while page:
        resp = requests.get(
            BASE,
            headers=HEADERS,
            params={
                "market": market,
                "make_id": make_id,
                "model_id": model_id,
                "sort": "price",
                "page": page,
                "limit": limit,
            },
            timeout=30,
        )
        if resp.status_code == 429:
            time.sleep(int(resp.headers.get("Retry-After", 1)))
            continue
        resp.raise_for_status()
        credits += int(resp.headers["X-Credits-Used"])
        data = resp.json()["data"]
        for item in data["listings"]:
            rows.append(
                {
                    "listing_id": item["listing_id"],
                    "price": item["price"]["amount"],
                    "km": item["mileage"]["value_km"],
                    "year": item["first_registration"]["year"],
                    "city": item["location"]["city"],
                }
            )
        page = data["pagination"]["next_page"] if data["pagination"]["has_more"] else None
    return rows, credits


rows, credits = sweep(13, 1639)
print(len(rows), "listings,", credits, "credits")

The 429 branch waits for the Retry-After header and retries the same page, as described in the rate limits docs. Free accounts get 1 request a second, so a loop like this is fine as written. Because requests have no idempotency key, a timeout may have succeeded and been billed, so check X-Credits-Remaining if you retry after one. The errors page covers the other failure codes.

What does the sweep cost in credits?

Every call is 1 credit, so the cost is the number of pages. As an example, suppose you track 50 make and model pairs and each runs to 10 pages: 50 × 10 = 500 calls × 1 credit = 500 credits a month. Add 10 taxonomy calls to refresh the model lists and the month is 510 credits, inside the 1,000 free credits that come with a verified email address.

The pair count and the pages per pair are assumptions for this example. Your real numbers depend on how many results each filter returns, which you see in pagination.total after the first call. Failed calls return X-Credits-Used: 0, and X-Credits-Remaining shows your balance after every call. See credits for how billing works and pricing for what credits cost.

Which market and filters should you use?

Choose the market that matches where you want listings, since market takes one of nine values: de, at, be, it, fr, nl, es, com or pl. In the recorded de response the listing URLs are on autoscout24.de and the transmission text is German (Schaltgetriebe), so keep the market fixed within one sweep and store it in each row.

Two filters keep sweeps comparable. Fix sort and order so pages don't reshuffle between runs, and use registration_year_min and registration_year_max to compare cars of the same age. Use seller_type to separate dealer from private listings, so a dealer average and a private-seller average aren't blended into one number. That's our suggestion, not something the data requires.

If you already know a dealer, dealer-inventory takes a dealer_slug and pages through that dealer's stock at the same 1 credit per call. In the recorded response, the dealer listing carries a dealer_slug in its seller object, which you can pass to the dealer endpoints.

Is the official route a better fit?

If you are a dealer or partner of AutoScout24 and need data about your own stock, ask AutoScout24 directly what it offers through its own sites, such as AutoScout24 Germany or AutoScout24 international; we don't document its programmes here. Perceptlayer's endpoint is for reading public listings across a market, and the data is collected from publicly available sources and returned as found at the time of the call.

For what to do with the fields once you have them, used car listing data for price analysis covers which ones matter. The wider Cars APIs page lists the other sources in this area, and the all APIs index has the full catalogue of 248 websites and 1,659 endpoints.

What should you do next?

Create a free key, open the search endpoint in the playground, and run the BMW request above with your own make and market. Then call taxonomy once to swap in your IDs, and wrap the sweep script around your list. Calls on this API cost 1 credit each, so you can check the result against X-Credits-Remaining as you go. Get a free API key.

APIs in this post

Frequently asked questions

Is there an official AutoScout24 API?

We don't cover any AutoScout24 programme here, so ask AutoScout24 directly if you need an official feed. The Perceptlayer endpoint returns public listing data as JSON, collected from publicly available sources, and is billed at 1 credit per successful call.

How much does an AutoScout24 search cost?

A successful call to search costs 1 credit, and so does every other endpoint on this API. Failed calls cost nothing and return X-Credits-Used: 0. Ten pages for one make and model is 10 credits. Every account gets 1,000 free credits a month after verifying an email address, with no credit card.

How do I page through all the results?

Read the pagination object in each response. Keep requesting next_page while has_more is true, and stop when it is false. In the recorded response, total was 193 and total_pages was 10 at a limit of 20, so you would make 10 calls, each at 1 credit.

What happens if I send an invalid parameter?

You get an HTTP 422 with the code invalid_parameters and a JSON body of {"error": {"code", "message", "request_id"}}. Failed calls aren't billed. Check the parameter against the allowed values in the table above, and take make_id and model_id from the taxonomy endpoint. The errors page lists every code.

Can an AI agent call this endpoint?

Yes. The MCP server at https://api.perceptlayer.com/mcp exposes search_endpoints, get_endpoint and call_endpoint. An agent can look up the AutoScout24 search endpoint for free, then call it with an endpoint ID and parameters, spending 1 credit on success only. See AI agents (MCP) for setup.

  1. Cars.com API: Used Car Listings and Price Evaluations

    Search Cars.com listings with its six required parameters (zip, radius_miles, stock_type, sort, page, page_size), then listing, dealer and price-eval calls.

  2. AutoTrader UK API: Get Used Car Listings as JSON

    Search UK used car listings by postcode, make and price with one GET call. Real BMW response, 6 endpoints, 1 credit per call, failed calls free.

  3. CarGurus API: Search Used Car Listings and Dealers

    Get CarGurus used car listings as JSON: look up make and model IDs, run search at 1 credit a call, then pull a dealer by service_provider_id.

Start with 1,000 free credits

No password to set. We email you a sign-in link, and your key works on every API.

No credit card. 1,000 free credits a month.