Your coding assistant is guessing MarketCheck parameters. Hand it the documented list
A free, keyless Docs MCP server, a drop-in AGENTS.md and an OpenAPI 3.1 spec put the documented parameters in front of your assistant, and the same spec can type your client and fail CI on a guessed name.
MarketCheck5 min read
For developers building on the MarketCheck API with an AI coding assistant
A coding assistant that has not seen the MarketCheck docs writes calls from what similar APIs look like. A guessed max_price is not in the parameter list at all (the documented filter is price_range), rows=100 returns the default page of 10 without an error because the docs cap page size at 50, and the key has to travel as the api_key query parameter on every request.
Since Sep 03, 2026 the developer portal has published four things for coding assistants: the Docs MCP server, an OpenAPI 3.1 spec, an AGENTS.md conventions file and an llms.txt index. All four are free and keyless.
In short. Add
https://developers.marketcheck.com/api/docs-mcpto your coding assistant and drop AGENTS.md into the repo, and it looks parameters up before it writes a call. Then useopenapi.jsontwice: generate a typed client, so a guessed parameter fails to compile, and run a CI check that fails on any undocumented parameter in the URLs your code builds.
What the Docs MCP gives your assistant
It is an MCP server over HTTP with five read-only tools:
search_docsfor semantic search over the documentation and the published API policies.get_doc_pagefor a full documentation page as Markdown.list_endpointsfor every documented endpoint with its method, path, region and docs URL.get_endpointfor one endpoint's parameters, with a documented sample request and response.get_plans_and_limitsfor the current published plans and their limits.
It reads public documentation, so there is no sign-in and no key, and the portal says using it does not count against your plan. Live vehicle data is the job of the Data MCP, which needs one of your API keys and bills like the REST API.
Install it
Claude Code takes one command. VS Code has a command-line equivalent, or you can add the same entry under "servers" in .vscode/mcp.json.
claude mcp add --transport http marketcheck-docs https://developers.marketcheck.com/api/docs-mcp
code --add-mcp '{"name":"marketcheck-docs","type":"http","url":"https://developers.marketcheck.com/api/docs-mcp"}'Cursor reads .cursor/mcp.json in your project, or ~/.cursor/mcp.json for every project:
{
"mcpServers": {
"marketcheck-docs": { "url": "https://developers.marketcheck.com/api/docs-mcp" }
}
}Windsurf reads ~/.codeium/windsurf/mcp_config.json and names the key serverUrl:
{
"mcpServers": {
"marketcheck-docs": { "serverUrl": "https://developers.marketcheck.com/api/docs-mcp" }
}
}The same snippets, with copy buttons, are on the MCP page.
Use 1: the request, looked up before it is written
- You ask for a call
- get_endpoint
- Assistant writes the request
- You review the diff
With the server connected, a prompt such as "Which parameters does /v2/search/car/active accept for price and mileage ranges? Show an example." sends the assistant to get_endpoint. This is what comes back, trimmed to four parameter rows:
# GET https://api.marketcheck.com/v2/search/car/active
Inventory Search (NA). Docs: https://docs.marketcheck.com/docs/api/cars/inventory/inventory-search
## Query parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| api_key | string | yes | | Your MarketCheck API authentication key. Required for every request, unless OAuth is used. |
| miles_range | string | | | Filters listings by odometer reading. Specify as `min-max` miles (e.g., `1000-50000`). |
| price_range | string | | | Filters listings by advertised price in USD. Specify as `min-max` (e.g., `1000-50000`). |
| year_range | string | | | Filters listings by model year range. Specify as `min-max` (e.g., `2015-2025`). |The assistant now writes price_range=5000-20000 as a string in min-max form, because the table says so. The full result lists every parameter with its type, required flag, default and description, then the documented sample request and a truncated sample response.
Use 2: the edge cases, in every session
- AGENTS.md in the repo root
- search_docs and get_doc_page
- Wrapper with paging and 429 rules
- Code review
The rules that break a wrapper, on paging and on rate limits, sit deep in long reference pages, and AGENTS.md carries them into every session. Put it in your project root and any assistant that reads AGENTS.md or CLAUDE.md picks up the auth rule, the pagination rules, rate-limit handling and the key endpoints:
curl -O https://developers.marketcheck.com/AGENTS.mdThe rules it carries all come from the docs:
rowstops out at 50 on Inventory Search, and a larger value falls back to the default of 10 with no error.- Paging past the depth your plan allows, or past
num_found, returns a 422. Treat that as the end of the results. - A 429 has two causes.
RateLimit-*headers describe the per-second window andQuota-*headers the monthly quota. HonorRetry-After, and stop when the quota is spent, because waiting will not bring it back. - The gateway ends a request at 120 seconds, and the docs say timed-out requests are not charged.
Two example prompts on the MCP page exercise all of this: "Write a Python client for the MarketCheck history-by-VIN endpoint with 429 handling per the documented Retry-After rules" and "Build a TypeScript function that pages through all results of an inventory search, stopping correctly at the documented deep-pagination limit."
Use 3: development runs sized to your plan
- get_plans_and_limits
- Call budget in config
- Dev runs on a handful of VINs
get_plans_and_limits returns the current published plans, so the assistant can size a development run to the plan you are on. On the Free tier that means 500 calls a month at 5 calls per second, with a 100-mile radius cap. The cap is hard: past 500, calls return a 429 until the next monthly cycle, and nothing is charged. Free is licensed for evaluation and development only. Ask the assistant to put a per-run call budget in configuration and to cache responses while you iterate.
Unusual: a typed client generated from the spec
- openapi.json
- openapi-typescript
- Typed client
- Type check in CI
https://developers.marketcheck.com/openapi.json is an OpenAPI 3.1 description of every endpoint and parameter, with example responses taken from the docs. Feed it to a code generator and a guessed parameter stops compiling. With openapi-typescript for the types and openapi-fetch for the client:
npx openapi-typescript https://developers.marketcheck.com/openapi.json -o src/marketcheck-api.d.tsimport createClient from 'openapi-fetch'
import type { paths } from './marketcheck-api'
export const mc = createClient<paths>({ baseUrl: 'https://api.marketcheck.com/v2' })
// The spec declares api_key as a query-string security scheme, so add it to every request
mc.use({
onRequest({ request }) {
const url = new URL(request.url)
url.searchParams.set('api_key', process.env.MARKETCHECK_API_KEY ?? '')
return new Request(url, request)
}
})
export async function usedUnder20k(zip: string) {
const { data, error } = await mc.GET('/search/car/active', {
params: { query: { zip, radius: 50, car_type: 'used', price_range: '5000-20000', sort_by: 'price', rows: 50 } }
})
if (error) throw new Error('Inventory Search failed')
// Response bodies are typed unknown: declare only the fields you read
return data as { num_found: number, listings: { vin: string, price?: number }[] }
}
// A guessed name fails the type check before it reaches the API:
// mc.GET('/search/car/active', { params: { query: { max_price: 20000 } } })
// error TS2353: Object literal may only specify known properties, and 'max_price' does not exist in type ...The spec types every request parameter, including path parameters such as {vin} on /history/car/{vin}. Responses in the spec are examples without schemas, so generated response bodies come through as unknown. Declare the handful of fields you read, as the cast above does.
Unusual: a CI check that fails on an undocumented parameter
Python scripts and hand-built URL strings can still carry a guessed name. This check reads the URLs your code builds: collect the request URLs your integration tests produce, or a day of staging requests with the key values stripped, and fail the build when one of them uses a path or query parameter the spec does not document.
- Every pull request
- URLs your tests build
- Pinned openapi.json
- Checker fails the build
- Fix the call
// Fails CI when a MarketCheck URL uses a path or query parameter the spec does not document.
// node check-params.mjs openapi.json captured-urls.txt
import { readFileSync } from 'node:fs'
const [specFile, urlFile] = process.argv.slice(2)
const spec = JSON.parse(readFileSync(specFile, 'utf8'))
const deref = p => (p.$ref ? spec.components.parameters[p.$ref.split('/').pop()] : p)
// api_key is declared as a security scheme, not as a parameter of each operation
const keyParams = Object.values(spec.components.securitySchemes).filter(s => s.in === 'query').map(s => s.name)
const routes = Object.entries(spec.paths).map(([template, ops]) => ({
template,
pattern: new RegExp(`^${template.replace(/\{[^}]+\}/g, '[^/]+')}$`),
params: new Set([...keyParams, ...Object.values(ops)
.flatMap(op => (op.parameters ?? []).map(deref))
.filter(p => p.in === 'query')
.map(p => p.name)])
}))
let failures = 0
for (const line of readFileSync(urlFile, 'utf8').split('\n').filter(Boolean)) {
const url = new URL(line)
const path = url.pathname.replace(/^\/v2/, '') // the spec's server URL already ends in /v2
const route = routes.find(r => r.pattern.test(path))
if (!route) { console.error(`undocumented path: ${path}`); failures++; continue }
for (const name of new Set(url.searchParams.keys())) {
if (!route.params.has(name)) { console.error(`${route.template}: undocumented parameter "${name}"`); failures++ }
}
}
console.log(failures ? `${failures} problem(s)` : 'every parameter is documented')
process.exit(failures ? 1 : 0)Fed a URL with max_price and limit, it prints both names against /search/car/active and exits with 1. A misspelled path such as /v2/search/cars/active fails the same way.
Limits to design around
- The Docs MCP answers questions about the API. Queries against live data go through the REST API or the Data MCP.
- The spec is a snapshot. Its
info.versionends in its build date,2.0.0-20260903for the current build. Commit a copy for CI and refresh it on purpose. When something seems to be missing, ask the assistant to read the page withget_doc_page, which fetches it from docs.marketcheck.com, and note that any docs URL returns raw Markdown with.mdappended. - The key is a security scheme. The spec declares
api_keyonce, as a query-string scheme, so generated clients need the middleware above, and the checker adds it to every path. - One file, two regions. UK endpoints sit in the same spec with
ukin the path, such as/search/car/uk/active, and their pages live under/uk/docs. - Keys stay out of prompts. Read the key from an environment variable, and keep it out of chats and commits.
- The URL check sees only what your tests call. Code paths your tests never exercise go unchecked, so pair it with the typed client where you can.
Who this is for
Anyone writing MarketCheck integration code with Claude Code, Cursor, VS Code or Windsurf, and the tech lead who reviews it. The typed client suits TypeScript teams, and the URL check suits mixed-language repos and scripts that live outside the main codebase.
Start here
- Add the Docs MCP with the command or snippet for your editor.
- Run
curl -O https://developers.marketcheck.com/AGENTS.mdin your project root. - Create a free account for a key. The Free tier gives you 500 calls a month for development.
- Generate the typed client, then add the parameter check to CI.
- The snippets are also on the MCP page, and the full reference lives at docs.marketcheck.com.