Skip to content
Guides / Test with test keysView Markdown ↗
Beta

AutoUncle Data Services is in beta. The API, MCP tools and agent skill may change often, including breaking changes, until version 1.0. Read the changelog before you upgrade an integration.

Test with test keys

Test keys let you build and test your whole integration for free: requests, refusals, retries, limits and result handling. This page explains exactly what test mode simulates, what it does not, and how to write tests you can rely on.

What a test key is

A test key starts with au_test_ and is created under API keys with Mode: Test · simulated, free. It uses the same endpoints, scopes, validation, catalogue and error contract as a live key. The difference is the valuation itself:

Test key Live key
Price Simulated by a deterministic formula Real market valuation
Credits Never One per valued result
precision.grade Always indicative high, medium, low or indicative
model.model_id / model.synthetic sandbox-v1 / true The production model / false
valuation.intervals[].method synthetic The model's method
AI interpretation of text and photos Never runs Runs when enabled and needed
Notes Includes synthetic_result No synthetic_result
Concurrency Its own limit of 4 per workspace 4 per workspace, plus shared market capacity
Fair-use allowance Valuations, selling-time forecasts, normalization and VIN, HSN/TSN and plate lookups share 10 per minute, 100 per hour and 300 per day per workspace None beyond the rate limits and credits
Idempotency keys and read-back Separate from live Separate from test
Request logs Recorded with mode test Recorded with mode live

Catalogue and account calls behave identically for both modes and are free either way.

Test keys are for testing

Test keys exist to build and test an integration. Normalization and the vehicle identification in a test valuation use the real catalogue, so a test key could look like a free way to identify real vehicles. It is not allowed: the service terms forbid using test keys to check, identify or value real vehicles for your business or your users, and AutoUncle monitors test-key usage per workspace and key.

To keep that fair, test-key requests that identify a vehicle share a fair-use allowance per workspace: 10 per minute, 100 per hour and 300 per day. Catalogue and account calls do not count. When a window is used up, the request is refused with 429 test_limit_reached and retry_after, before anything is recorded; a refused request does not count against the other windows. Keep test suites small, stub the API in unit tests, and contact us if your CI needs a higher allowance.

What the simulated price depends on

The test price is a deterministic function of the registration year, mileage, power and market. It does not depend on make, model or equipment, so a test Golf and a test Porsche with the same year, mileage and power get the same price. The intervals are fixed proportions of the price (±20% for 80%, ±35% for 95%, ±50% for 99%).

This is deliberate: test results let you check your parsing, display and error handling, never market logic. Never show a test price to users as a market value. Detect test results with mode: "test", model.synthetic: true or the synthetic_result note.

What you can test

Behaviour How to trigger it with a test key
A valued result A structured request with year and mileage
Ambiguity warnings and hints Send make and model without catalogue IDs: generation_ambiguous and drivetrain_unresolved
A pinned vehicle Send a catalogue drivetrain_id
missing_mileage refusal Leave out vehicle.mileage
Mileage baseline Leave out mileage and set options.allow_mileage_estimation: true
Assumed equipment Set options.allow_equipment_estimation: true. Test keys assume only a catalogue trim's equipment, never similar vehicles' (equipment_not_assumed otherwise)
Assumed year Leave out the year (best-effort resolution): year_imputed
precision_below_minimum refusal Set options.min_precision to low or higher
Strict refusals options.resolution: "strict" without a year or mileage
catalogue_id_conflict Send a generation_id and a drivetrain_id from a different generation
unknown_catalogue_id Send a catalogue ID that does not exist
unsupported_vehicle Set vehicle.flags.is_damaged: true
invalid_request with pointers Send an unknown field, or a string where a number is expected
Idempotent replay Send the same body twice with the same key
idempotency_conflict Send a different body with a used key
images_not_used Add images; test mode never reads them
country_not_enabled Value in a market your workspace or key does not allow
Rate limiting Exceed 60 valuation requests in a minute (this also slows your other test traffic)

What you cannot test

  • Real prices, grades and intervals. Only a live key can show how precise results are for your vehicles.
  • Free-text and photo interpretation. Test mode only runs deterministic text rules, so a description on its own is usually refused (vehicle_not_identified or missing_mileage). Use structured attributes in tests, and judge text interpretation with a live key.
  • Dependency failures such as engine_timeout or resolution_unavailable. Simulate them in your own code with a stub that returns the documented error bodies from the error reference.
  • Credit exhaustion. Test keys never spend credits, so insufficient_credits cannot happen. Stub it.
  • engine_busy. Test valuations never use live capacity.

Writing reliable tests

  • Assert on structure and codes, not on prices. Check status, error.code, precision.grade, the presence of valuation.intervals and your handling of warnings. Test prices change from one year to the next because they depend on the vehicle's age.
  • Use unique idempotency keys per test run, for example including a run ID. A key reused across runs replays the first run's result, which is useful for replay tests and confusing everywhere else.
  • Keep CI under the limits. Run valuation tests with at most four in parallel and well under 60 per minute, or you will see concurrency_limited and rate_limited (both free and safe to retry after retry_after).
  • Stub the API in unit tests with recorded responses, and keep a small set of contract tests that call the real API with a test key.
  • Keep test keys out of production and live keys out of CI. Name keys by environment so the console's usage and logs stay readable.

A minimal contract test

import os
import uuid

import requests

API = "https://data-services.autouncle.com"
HEADERS = {"Authorization": f"Bearer {os.environ['AUTOUNCLE_API_KEY']}"}


def value(body, key):
    return requests.post(f"{API}/v1/valuations", json=body, headers={**HEADERS, "Idempotency-Key": key}, timeout=30)


def test_valuation_contract():
    run = uuid.uuid4().hex
    body = {"country": "DK", "vehicle": {"make": "VW", "model": "Golf", "model_year": 2020,
                                         "mileage": {"value": 60000, "unit": "km"}}}

    first = value(body, f"ci-{run}-golf")
    assert first.status_code == 200
    result = first.json()
    assert result["status"] == "valued"
    assert result["mode"] == "test"
    assert result["precision"]["grade"] == "indicative"
    assert any(note["code"] == "synthetic_result" for note in result["notes"])

    replay = value(body, f"ci-{run}-golf").json()
    assert replay["request_id"] == result["request_id"]
    assert replay["usage"]["idempotent_replay"] is True

    refused = value({"country": "DK", "vehicle": {"make": "VW", "model": "Golf", "model_year": 2020}}, f"ci-{run}-no-km")
    assert refused.status_code == 422
    assert refused.json()["error"]["code"] == "missing_mileage"

Run it with pytest and a test key in AUTOUNCLE_API_KEY.

The playground

The console playground preselects a test key, so you can try any request by hand before writing code. Live keys are labelled consumes credits on success in the key picker.

Next steps