Porsche US (PCNA) Public API Sources
Porsche Cars North America (PCNA) exposes vehicle and dealer data through four separate services. All are unauthenticated but the Finder endpoints require a realistic browser User-Agent (HTTP 429 is returned without one).
Postman: Porsche public APIs — US
Endpoint Overview
| # | Endpoint | Method | URL | Primary data |
|---|---|---|---|---|
| 1 | MPI Models API | GET | https://mpi.pccompare.aws.porsche-preview.cloud/markets/{marketCode}/models/ | Per-market model catalog (82 entries for PCNA); GET /markets/ lists all 113 market codes + locales |
| 2 | Finder Inventory Search | GET | https://finder.porsche.com/us/en-US/search | Paginated vehicle listings (RSC format); filterable by dealership, condition, model |
| 3 | Finder Hitcounts API | GET | https://finder.porsche.com/api/us/en-US/hitcounts | Live facet counts: model series, condition, dealers with inventory |
| 4 | Finder Vehicle Detail / Features | GET | https://finder.porsche.com/us/en-US/details/{listingUrlSlug} | Per-listing equipment codes and options |
| 5 | Dealer Microsite Inventory | GET | https://{dealer-subdomain}.porsche.com/us/en-US/search/{condition} | Per-dealer inventory; no page cap |
| 6 | Dealer Search | GET | https://www.porsche.com/us/en-US/dealersearch/ | All 217 PCNA dealers (Astro SSR HTML) |
| 7 | Leasing / Finance Data | GET | https://finder.porsche.com/api/us/en-US/leasing-finance-data | Rate estimates for all models |
| 8 | Car Configurator Catalog | GET | https://configurator.porsche.com/en-US/mode/model/{modelCode}.data | Per-model option/feature catalog (~160 options incl. standard equipment) |
1. MPI Models API
GET https://mpi.pccompare.aws.porsche-preview.cloud/markets/{marketCode}/models/
Pure JSON API, open CORS. No authentication or special headers required. The MPI
service is global, keyed by its own market codes (PCNA = USA). Returns the market's
full catalog unconditionally (82 active models for PCNA) — query parameters (e.g.
modelRange=911) are silently ignored. No pagination; always one page.
The modelType field is the stable model identifier used as the FK from inventory.
The MPI host exposes three nested levels — a market list, a per-market endpoint index, and the model catalog (the one the pipeline ingests):
GET https://mpi.pccompare.aws.porsche-preview.cloud/markets/ # all markets
GET https://mpi.pccompare.aws.porsche-preview.cloud/markets/{marketCode}/ # one market's endpoint index
GET https://mpi.pccompare.aws.porsche-preview.cloud/markets/{marketCode}/models/ # model catalog (ingested)
/markets/ — a JSON array of all Porsche markets (113 at discovery, 2026-07-17),
each with its MPI market code, country description, languages + languageCodes
(locales, e.g. en-US), and region — e.g.
{ "market": "PCNA", "description": ["USA", "US"], "languageCodes": { "en": ["en-US"] }, "region": "North America" }.
This is where marketCode values come from.
/markets/{marketCode}/ — the per-market node one level above models/. Its
links object enumerates every Porsche service URL for the market —
finder, dealerSearch, porscheCodeSearch (the configurator), compare, icc,
contactForm, modelOverview — and it carries the model hierarchy (ranges,
preSales, outOfProduction). It is the canonical source for the
finder/dealersearch/configurator base URLs this pipeline otherwise builds by hand
(for PCNA: finder → https://finder.porsche.com/us/en-US,
dealerSearch → https://www.porsche.com/us/en-US/dealersearch,
porscheCodeSearch → https://configurator.porsche.com/porsche-code/en-US). The model
data lives one level down, at models/.
Request
No required headers or parameters. Optional:
| Parameter | Effect |
|---|---|
modelRange | Silently ignored — always returns all 82 models |
Response shape
Top-level object { "models": [...] }. Each entry:
| Field | Type | Notes |
|---|---|---|
modelType | string | Primary model identifier (e.g. 9921B2); FK target from inventory.modelCode |
modelSeriesId | string | Slug (e.g. carrera-models) |
modelRangeId | string | Range slug (e.g. 911) |
modelName | string | Display name (e.g. 911 Carrera) |
modelYear | string | 4-digit year string |
modelRange | string | Range display name |
modelSeries | string | Series display name |
ecoGroup | string | Marketing group |
price | object | { currencyCode, formattedValue, value } — base MSRP |
wheelDrive | string | e.g. Rear-Wheel Drive |
gearType | string | e.g. AUTOMATIC |
engineType | string | e.g. FUEL, ELECTRIC |
bodyType | string | e.g. Coupe, SUV |
fuelType | string | e.g. FUEL, BATTERY_ELECTRIC |
seats | string | e.g. 2+2 |
numberOfDoors | integer | |
technicalDetail | object | Power (HP/kW), acceleration, top speed, range, dimensions, weight |
colorGroups | array | Available exterior color options |
interiorDesignOptions | array | Available interior options |
options | array | Available model options |
images | object | sideShot and technicalTopShot image URLs |
6 modelCode values seen in live inventory (e.g. 991450) are absent from the MPI
catalog — these are historical/discontinued models. Expect a partial join between
inventory and models.
The verbatim response is captured at
projects/ai_porsche/tests/data/porsche_mpi_models_sample.json.
2. Finder Inventory Search
GET https://finder.porsche.com/us/en-US/search?condition=<c>&dealership=<id>&page=<n>
Next.js SSR endpoint. Returns paginated vehicle listings. A realistic Chrome
User-Agent header is required (HTTP 429 without one). The preferred protocol is
RSC format (_rsc=<random> query parameter + RSC: 1 header) which returns
text/x-component with clean unescaped inline JSON — simpler to parse than the
SSR HTML variant, which double-escapes all JSON.
Working query-param filters (verified 2026-07-12)
The bare /search route honors query-param filters:
| Parameter | Values | Effect |
|---|---|---|
dealership | A dealer's ppnOrgId (e.g. 6161) | Scopes results to exactly that dealer's stock; the returned listings' meta.seller.sellerId equals the requested id |
condition | new, used, porsche_approved, classic | Filters to that condition only — one condition per request; span conditions by calling once per value and merging on vin (stable cross-condition identity) |
model | Model-range slug (e.g. macan) | Optional; omitted = all model ranges |
page | 1-based integer | pages.totalPages in the response reports the page count for the filtered result set |
A dealership-scoped query returns the dealer's full multi-range inventory in a
handful of pages, so per-dealer retrieval never approaches the 100-page cap below.
Hard server-side page cap
Any single query is capped at 100 pages, regardless of request format or
sorting. Page 101+ returns activePage=101, totalPages=0, items=[]. Broad queries
(nationwide: ~1,843 pages; per {series} path: macan ~846, cayenne ~574, 911 ~185,
panamera ~108, taycan ~105) are only partially accessible — at 15 items/page, page
100 yields up to 1,500 listings per query. Narrow queries (e.g. one dealership)
stay far under the cap.
There is also a legacy /search/{series} path-segment variant (series slug in the
URL path). On that variant the 2026-06 probes found modelCode=, condition=,
modelSeries=, seller=, modelYear=, sortBy= silently ignored — note seller
is not a working alias for dealership.
Request
| Parameter / Header | Required | Notes |
|---|---|---|
User-Agent | Yes | Realistic Chrome UA required; 429 without |
_rsc query param | Recommended | Any random string triggers RSC format (text/x-component) |
RSC: 1 header | Recommended | Pair with _rsc for RSC format |
dealership / condition / model | Optional | See working filters above |
page | Optional | 1-based; defaults to 1; silently clamped to 100 |
Response shape (RSC format)
Lines in the text/x-component stream containing
"pages":{"activePage":N,"totalPages":N},"items":[...] are plain inline JSON.
Per-item fields:
| Field | Type | Notes |
|---|---|---|
id | string | Primary listing identifier (short alphanumeric, e.g. 5KGQNM) |
listingUrlSlug | string | URL slug for the detail endpoint (e.g. porsche-cayenne-gts-new-5KGQNM) |
meta.title | string | Display name (e.g. Cayenne GTS) |
meta.imageUrl | string | Lead image URL |
meta.detailsUrl | string | Full detail page URL |
meta.condition | string | new, used, or porsche_approved |
meta.priceCurrency | string | USD |
meta.priceValue | number | Listing price |
meta.vin | string | 17-digit VIN |
meta.modelYear | number | |
meta.model | string | Display model name |
meta.color | string | Exterior color display name |
meta.interiorColor | string | Interior color display name |
meta.transmission | string | Transmission type |
meta.engineType | string | combustion or electric |
meta.mileage | object | { value, unit } |
meta.seller | object | Seller name, address, sellerId, porschePartnerNumber |
dataLayerListingMeta.car.listingId | string | Same as id |
dataLayerListingMeta.car.modelCode | string | FK to mpi_models.modelType (e.g. 9YABS1) |
dataLayerListingMeta.car.modelRangeName | string | Series slug (e.g. cayenne) |
dataLayerListingMeta.car.modelModelYear | number | |
dataLayerListingMeta.car.isPorscheApproved | boolean | |
dataLayerListingMeta.partner.ppnOrgId | string | FK to dealers.ppnOrgId |
dataLayerListingMeta.partner.companyId | string | FK to dealers.porschePartnerNo (7-digit) |
dataLayerListingMeta.partner.zipCode | string |
The verbatim response is the raw RSC flight stream (text/x-component), captured
as projects/ai_porsche/tests/data/porsche_finder_inventory_raw.txt; the fields
above are the pages/items payload parsed out of that stream.
3. Finder Hitcounts API
GET https://finder.porsche.com/api/us/en-US/hitcounts
Pure JSON API. Realistic Chrome User-Agent header required (HTTP 429 without).
Returns live facet counts for the Finder: model series, condition values, and
sellers with active inventory. The condition[].key values are the accepted
condition= filter values on the search route, and sellers[].key values are
ppnOrgIds accepted by the dealership= filter — but since the search route can
be queried per dealer directly, the counts themselves are not needed to retrieve
inventory.
Response shape
| Field | Type | Notes |
|---|---|---|
hitcounts.modelSeries | array | [{ key, value }] — 12 series; key is the URL path segment |
hitcounts.condition | array | [{ key, value }] — new, used, porsche_approved, classic |
hitcounts.bodyType | array | [{ key, value }] — suv, coupe, etc. |
hitcounts.sellers | array | [{ key, value }] — 207 entries; key is ppnOrgId |
4. Finder Vehicle Detail / Features
GET https://finder.porsche.com/us/en-US/details/{listingUrlSlug}
Next.js RSC endpoint returning per-listing equipment (option codes and descriptions).
Realistic Chrome User-Agent required. Use RSC format (_rsc=<random> + RSC: 1)
for clean JSON output.
The {listingUrlSlug} comes from inventory.listingUrlSlug (e.g.
porsche-cayenne-gts-new-5KGQNM). The listing ID (inventory.id) is the suffix after
the last -.
Response shape
| Field | Type | Notes |
|---|---|---|
listingId | string | FK to inventory.id |
vin | string | 17-digit VIN |
modelCode | string | FK to mpi_models.modelType |
modelName | string | Display name |
modelYear | number | |
equipment.importantEquipmentOptions | array | [{ value, label }] — key highlight options |
equipment.specialEquipment | array | Equipment categories, each with { name, items: [{ code, name, description, images }] } |
The code field in specialEquipment[*].items[*] is the Porsche option code
(e.g. PU5 = Premium Package Plus, 3FU = Panoramic Roof, 6FU = Exterior Mirror Base).
The correct identity grain is (listingId, equipmentCode) — option codes are
per-listing, not per-model. For the per-model option catalog (all orderable
features), see the Car Configurator (§8).
The verbatim RSC detail stream is captured at
projects/ai_porsche/tests/data/porsche_finder_inventory_detail_raw.txt.
5. Dealer Microsite Inventory
GET https://{dealer-subdomain}.porsche.com/us/en-US/search/{condition}
97 Porsche dealers operate *.porsche.com microsites that serve the same Next.js
Finder but scoped to their own inventory. Unlike the national Finder, these sites
have no observed page cap (a dealer with 200 vehicles can be fully retrieved).
The dealer subdomain is not exposed in the main dealersearch response — it requires a
separate lookup (the Finder seller={ppnOrgId} filter or the dealer's known homepage
URL). This endpoint is documented for completeness; the pipeline uses the national
Finder for inventory ingestion.
| Parameter | Notes |
|---|---|
{dealer-subdomain} | e.g. porscheminneapolis |
{condition} | new, used, or omit for all |
page | 1-based; no observed cap on dealer microsites |
6. Dealer Search
GET https://www.porsche.com/us/en-US/dealersearch/
Astro SSR HTML page. No authentication or special headers required. Dealer data is
embedded as HTML-encoded JSON (" = double-quote) inside the Astro component
props attribute. Each dealer appears twice in the source — 434 total occurrences,
217 unique dealers.
Parse by extracting the encoded JSON string from the props attribute and decoding
HTML entities.
Response shape (after parsing)
| Field | Type | Notes |
|---|---|---|
id | string | UUID |
name | string | Dealership display name |
porschePartnerNo | string | 7-digit partner number; FK target from inventory.companyId |
ppnOrgId | string | Numeric org ID; FK target from inventory.ppnOrgId and hitcounts.sellers[].key; the value the Finder search dealership= filter accepts |
address | object | { street, postalCode, city, state, countryCode } |
facilityType | string | e.g. PORSCHE_CENTER |
contactDetails | object | { phoneNumber, emailAddress, homepage } |
coordinates | object | { latitude, longitude } |
Seller ppnOrgId=2795 appears in hitcounts (1 vehicle) but has no record in the
dealersearch page — this is a private seller, not a dealer.
The verbatim SSR page HTML is captured at
projects/ai_porsche/tests/data/porsche_dealersearch_dealers_sample.html.
8. Car Configurator Catalog
GET https://configurator.porsche.com/{locale}/mode/model/{modelCode}.data?_routes=customer-configurator
React Router single-fetch endpoint (.data suffix) returning the full per-model
option catalog as a turbo-stream payload (text/x-script, ~470 KB). No
authentication, no User-Agent enforcement (CloudFront-served — not behind the
Finder's Vercel WAF). Unknown model codes return 404 (text/x-script body).
The configurator app is global: the single {locale} path segment (en-US,
de-CH, …) selects both market and language. {modelCode} is the same code space
as mpi_models.modelType (e.g. 992892 = 911 GT3 S/C). The optional
_routes=customer-configurator query param narrows the payload to that route's
loader data (drops root/translation payloads, ~12% smaller).
Full-catalog sweep (2026-07-17, all 74 live MPI codes × en-US + de-CH): every 200
decodes with the same shape (67–250 options per model, catalog.orderType always
echoes the requested code, bundles always empty), and repeated requests return
identical decoded payloads. 11/74 codes 404 on en-US, 15/74 on de-CH — all
end-of-production models (every gas 718), market absences (gas Macans in CH), or
sold-out limited editions (911 GT3 90 F.A. Porsche, 911 Spirit 70). A 404 therefore
means "in the MPI catalog but no longer configurable", not an error.
The wire format is a flat JSON slot array with cross-references (the turbo-stream
format React Router embeds in SSR HTML via
window.__reactRouterContext.streamController.enqueue). Decoded, the
customer-configurator route's data carries:
| Field | Type | Notes |
|---|---|---|
catalog.orderType | string | The model code — FK to mpi_models.modelType |
catalog.modelName / modelYear / modelFamily / engineType | scalar | Model metadata |
market.currency | string | ISO currency for all prices (USD for en-US) |
views.search.options | object | Flat map of ~160 options keyed by option id |
views.search.ptsOptions | object | ~150 paint-to-sample colors (composite ids, e.g. 0UB.89.24931-24931_TD) |
Each views.search.options entry:
| Field | Type | Notes |
|---|---|---|
id | string | Porsche option code (e.g. F37, 24940, 2C6) |
title | string | Display name |
description | string | HTML fragment |
optionType | string | Single-letter type (I, Z, F, M, Q, L, C, P) |
family | string | Option family (e.g. AUSSEN_FARBE, RAD) |
sectionId / categoryId / categoryTitle / groupId / groupTitle | string | Catalog grouping |
isStandardEquipment / isBasicOption | bool | Standard-equipment flags |
priceNumeric | number | Price in market.currency (0 for no-cost options) |
The verbatim turbo-stream body is captured at
projects/ai_porsche/tests/data/porsche_configurator_features_raw.txt.
Cross-source identity keys
Value-matched FKs proven by live API sampling (2026-06-10):
| FK | Source field | Target field | Example | Coverage |
|---|---|---|---|---|
| inventory → dealers | dataLayerListingMeta.partner.ppnOrgId | dealers.ppnOrgId | 1500007384 | 100% (3-page sample) |
| inventory → dealers (alt) | dataLayerListingMeta.partner.companyId | dealers.porschePartnerNo | 4500679 | 100% (3-page sample) |
| inventory → models | dataLayerListingMeta.car.modelCode | mpi_models.modelType | 95BAU1 | 27/33 codes seen; 6 historical absent from live MPI |
| hitcounts → dealers | hitcounts.sellers[].key | dealers.ppnOrgId | 13846 | 206/207 match; 1 private seller |
search dealership= param → dealers | query param value | dealers.ppnOrgId | 6161 | Returned listings' meta.seller.sellerId equals the requested id (verified 2026-07-12) |
| detail → inventory | listingId (from URL slug suffix) | inventory.id | V3XXVK | URL slug always encodes listing ID |
| configurator → models | catalog.orderType | mpi_models.modelType | 992892 | Same code space; MPI codes drive the configurator fan-out (verified 2026-07-17) |
Availability and WAF notes
| Endpoint | UA required | Without UA | Notes |
|---|---|---|---|
| MPI Models API | No | 200 | Open CORS; pure JSON |
| Finder Inventory (SSR or RSC) | Yes | 429 | Realistic Chrome UA required |
| Finder Hitcounts | Yes | 429 | Realistic Chrome UA required |
| Finder Vehicle Detail | Yes | 429 | Realistic Chrome UA required |
| Dealer Microsite Inventory | Yes | 429 | Same Finder WAF rules |
| Dealer Search | No | 200 | Astro SSR; no UA enforcement |
| Car Configurator | No | 200 | CloudFront; no UA enforcement; 404 on unknown model code |