Skip to content
Partner Passport

For developers

Build with Partner Passport.

A public API for consistent publisher information, designed for affiliate networks and their integrations.

View example profile

Getting started

Reads are public: no API key or login is required. The intended base URL is https://partnerpassport.io/api/v1. Use your local app URL during development; these routes must be deployed before the public URL will work.

Lookups use GET. Public profile routes return HTML; API routes return JSON and support HEAD and OPTIONS. Only verified partners are returned. Public partnerships contacts and social profile links are included; account login emails and memberships are not. Network identifiers remain available in JSON for integrations but are not displayed on the public passport. They are self-reported, not verified by the networks.

HEAD uses the same lookup, status and headers as GET but returns no body. OPTIONS returns 204 without checking whether the publisher exists.

Examples show the database-backed handlers’ response shape with fictional values. See responses and integration notes. You can also view the separate example profile.

Publisher lookups

The sample public profile is available at /example, showing a fictional publisher.

Public profile by passport number

GET /partner/{passportNumber}

Parameters

passportNumber
Six letters or digits, case-insensitive (for example AB12CD). Use passportNumber, not the partnerId UUID.

Example response · 200 OK · HTML

Abbreviated HTML showing the profile content. The full page includes the Partner Passport layout. Values are illustrative.

<main>
  <h1>Wanderline Digital</h1>
  <p>wanderline.example</p>
  <section>
    <h2>About the publisher</h2>
    <p>A fictional travel and lifestyle publisher.</p>
  </section>
</main>

JSON API by passport number

GET /api/v1/partner/{passportNumber}

Parameters

passportNumber
Six letters or digits, case-insensitive (for example AB12CD). Use passportNumber, not the partnerId UUID.

Example response · 200 OK · JSON

Fictional values matching the database-backed public profile response. The profile is returned directly, without a data wrapper.

{
  "partnerId": "b9ac4d64-19ec-44db-88d1-48b7c72f21c2",
  "passportNumber": "AB12CD",
  "status": "verified",
  "displayName": "Wanderline Digital",
  "domain": "wanderline.example",
  "logoUrl": "https://partnerpassport.io/api/partners/AB12CD/logo",
  "website": "https://wanderline.example",
  "description": "A fictional travel and lifestyle publisher.",
  "markets": [
    {
      "id": 1,
      "code": "GB",
      "name": "United Kingdom"
    }
  ],
  "sectors": [
    {
      "id": 3,
      "name": "Travel"
    }
  ],
  "promotionMethods": [
    {
      "id": 1,
      "name": "Content"
    }
  ],
  "audience": {
    "monthlyVisitors": 25000,
    "socialFollowers": 8000
  },
  "contacts": {
    "partnerships": "partners@wanderline.example",
    "socialLinks": {
      "facebook": "",
      "instagram": "",
      "tiktok": "",
      "youtube": "",
      "linkedin": ""
    }
  },
  "networks": [
    {
      "id": 1,
      "code": "example-network",
      "name": "Example Network",
      "externalPartnerId": "DEMO-1042"
    }
  ],
  "demographics": {
    "ageGroups": [],
    "socioEconomicGroups": [],
    "householdGroups": [],
    "interests": [],
    "genderSplit": null,
    "primaryCountry": {
      "id": 1,
      "code": "GB",
      "name": "United Kingdom"
    },
    "reachType": "National",
    "regions": [],
    "otherInterest": "",
    "lastUpdated": null
  },
  "lastUpdated": "2026-09-17T12:00:00Z"
}

Public profile by network-specific ID

GET /partner/{network}/{id}

Parameters

network
The network's configured code: lowercase letters, digits, underscores or hyphens. Not its numeric ID.
id
The externalPartnerId within that network, matched exactly and case-sensitively. Treat as a string, preserve leading zeros and URL-encode it as a path segment.

Example response · 200 OK · HTML

Abbreviated HTML showing the profile content. The full page includes the Partner Passport layout. Values are illustrative.

<main>
  <h1>Wanderline Digital</h1>
  <p>wanderline.example</p>
  <section>
    <h2>About the publisher</h2>
    <p>A fictional travel and lifestyle publisher.</p>
  </section>
</main>

JSON API by network-specific ID

GET /api/v1/partner/{network}/{id}

Parameters

network
The network's configured code: lowercase letters, digits, underscores or hyphens. Not its numeric ID.
id
The externalPartnerId within that network, matched exactly and case-sensitively. Treat as a string, preserve leading zeros and URL-encode it as a path segment.

Example response · 200 OK · JSON

Fictional values matching the database-backed public profile response. The profile is returned directly, without a data wrapper.

{
  "partnerId": "b9ac4d64-19ec-44db-88d1-48b7c72f21c2",
  "passportNumber": "AB12CD",
  "status": "verified",
  "displayName": "Wanderline Digital",
  "domain": "wanderline.example",
  "logoUrl": "https://partnerpassport.io/api/partners/AB12CD/logo",
  "website": "https://wanderline.example",
  "description": "A fictional travel and lifestyle publisher.",
  "markets": [
    {
      "id": 1,
      "code": "GB",
      "name": "United Kingdom"
    }
  ],
  "sectors": [
    {
      "id": 3,
      "name": "Travel"
    }
  ],
  "promotionMethods": [
    {
      "id": 1,
      "name": "Content"
    }
  ],
  "audience": {
    "monthlyVisitors": 25000,
    "socialFollowers": 8000
  },
  "contacts": {
    "partnerships": "partners@wanderline.example",
    "socialLinks": {
      "facebook": "",
      "instagram": "",
      "tiktok": "",
      "youtube": "",
      "linkedin": ""
    }
  },
  "networks": [
    {
      "id": 1,
      "code": "example-network",
      "name": "Example Network",
      "externalPartnerId": "DEMO-1042"
    }
  ],
  "demographics": {
    "ageGroups": [],
    "socioEconomicGroups": [],
    "householdGroups": [],
    "interests": [],
    "genderSplit": null,
    "primaryCountry": {
      "id": 1,
      "code": "GB",
      "name": "United Kingdom"
    },
    "reachType": "National",
    "regions": [],
    "otherInterest": "",
    "lastUpdated": null
  },
  "lastUpdated": "2026-09-17T12:00:00Z"
}

JSON API domain search

GET /api/v1/partner?domain={domain}&page=1&pageSize=10

Parameters

domain
Required query parameter, supplied exactly once. Case-insensitive literal substring of the stored domain (1–253 characters after trimming). Use ASCII/punycode for international domains. Percent signs, underscores and backslashes are literal, not SQL wildcards. Missing, repeated, blank, overlong or control-character input returns 400. Results are sorted by domain. No matches or a page beyond the results returns an empty data array with pagination metadata.
page
Optional page number, default 1. A single integer from 1 to 2147483647. Invalid or repeated values return 400.
pageSize
Optional results per page, default 10. A single integer from 1 to 100. Invalid or repeated values return 400.

Example response · 200 OK · JSON

Returns matching verified partner summaries in a data array, sorted by domain, with pagination metadata: page, pageSize, total and totalPages. Defaults to page 1 with 10 results; pageSize can be 1–100. No matches or an out-of-range page returns 200 with an empty data array; no matches has totalPages 0. Use passportNumber to fetch the full profile.

{
  "data": [
    {
      "partnerId": "b9ac4d64-19ec-44db-88d1-48b7c72f21c2",
      "passportNumber": "AB12CD",
      "displayName": "Wanderline Digital",
      "domain": "wanderline.example",
      "status": "verified",
      "description": "A fictional travel and lifestyle publisher."
    }
  ],
  "pagination": {
    "page": 1,
    "pageSize": 10,
    "total": 1,
    "totalPages": 1
  }
}

The network lookup uses a lowercase network code and an external partner ID. Returned network objects include id, name, code and externalPartnerId. “example-network” and all sample IDs are fictional, not working demo lookups.

Domain search uses one required domain query parameter. It matches a literal part of the stored domain, ignoring case; for example, “example” matches “example.com” and “myexample.co.uk”. Use ASCII/punycode when searching international domains. SQL wildcard characters are treated literally. Missing, repeated, blank, overlong or control-character input returns 400. Use optional page (1–2147483647, default 1) and pageSize (1–100, default 10) to navigate results. Each parameter must appear at most once and use a positive integer without leading zeros; invalid values return 400. The response includes total matches and total pages. No matches or a page beyond the results returns 200 with an empty data array. Results may change between requests.

GET /api/v1/partner?domain=wander&page=1&pageSize=10

Publisher logos

GET /api/partners/{passportNumber}/logo

Both JSON lookups include logoUrl: an absolute, stable URL, or null when no logo is uploaded. Use the returned URL directly; this endpoint is outside the /api/v1 prefix. Replacements change the image, not its URL. The link does not expire.

The passport number is six letters or digits, case-insensitive. No login is needed for verified publishers. GET returns PNG, JPEG or WebP bytes (up to 2 MiB), not JSON. Display with object-fit: contain to preserve proportions. HEAD returns the same status and headers without a body; OPTIONS returns 204 without a lookup.

Unknown, invalid or non-verified publishers and absent logos return 404 with NOT_FOUND / “Logo not found.” Storage or database failures return 500 with INTERNAL_ERROR / “Unable to load logo.” Errors use the standard JSON error envelope. Responses use Cache-Control: no-store, Access-Control-Allow-Origin: * and allow GET, HEAD and OPTIONS. Images also send X-Content-Type-Options: nosniff and a restrictive Content-Security-Policy.

Profile fields

Passport-number and network lookups return the full profile object, with no data wrapper. Domain search returns a data array containing partnerId, passportNumber, displayName, domain, status and description for each match. Missing selections use empty arrays; audience counts and update timestamps can be null.

Markets, sectors, promotion methods and demographics contain named option objects, not bare IDs. Country objects also include a nullable code. Full profile responses include a nullable logoUrl but do not include hostname or image bytes.

Demographics now contains ageGroups, socioEconomicGroups, householdGroups and interests as id/name arrays. The old countries, customerLifecycles, recentEngagements, purchaseCategories and purchaseReadiness fields have been removed. primaryCountry is one country object or null; reachType is National, Regional, Local or null. regions contains up to five free-text locations (100 characters each). otherInterest is required when Other is selected and otherwise empty (maximum 200 characters).

genderSplit is null when unspecified. Otherwise women, men, other and unknown are all numbers between 0 and 100, with at most one decimal place and a combined total of 100. New saves allow up to five interests, including Other; previously saved selections may exceed five until the publisher edits them. Matching interests are retained; Business is no longer offered. New demographic fields start blank. lastUpdated remains the audience save timestamp.

partnerId
Registered partner UUID. Not the identifier used for passport-number lookup.
passportNumber
Six-character passport number used in public profile and API URLs.
status
Only verified partners are returned. Network identifiers remain self-reported.
displayName
Registered partner name.
domain
Registered partner domain, without a scheme or path.
logoUrl
Absolute, stable URL for the current logo, or null when no logo has been uploaded. Does not expire; the image changes when replaced.
website
Publisher website URL. Empty string if not supplied.
description
Publisher description. Empty string if not supplied.
markets
Country objects with numeric id, name and nullable code. Empty when none are selected.
sectors
Sector objects with numeric id and name.
promotionMethods
Promotion-method objects with numeric id and name.
audience
Reported audience counts. Each value is a number or null when not supplied.
contacts
Public partnerships email (not the login email) and socialLinks for facebook, instagram, tiktok, youtube and linkedin. All five social keys are always present; an empty string means not supplied. Social URLs must use HTTP/HTTPS on the corresponding platform, without credentials, and be at most 2048 characters.
networks
Self-reported mappings with id, name, code and externalPartnerId. External IDs are strings, preserving leading zeros.
demographics
Age groups, socio-economic and household groups, top interests (id/name arrays), optional gender percentages, primary country, reach type, up to five regions/cities, other-interest text and lastUpdated. Replaces the previous lifecycle, engagement and purchase-intent fields. New saves allow up to five interests; older selections may exceed five until edited.
lastUpdated
Profile metadata update timestamp, or null if metadata has not been saved.

Responses and availability

  • 200 · Success. A verified partner’s HTML profile or JSON profile object; domain search returns a data array of summaries, possibly empty.
  • 400 · Invalid search. Domain search requires exactly one non-blank term of 1–253 characters without control characters, with valid page and pageSize parameters if supplied.
  • 404 · Not found. Unknown, invalid or non-verified passport-number/network profiles. HTML routes show a not-found page; API routes return NOT_FOUND.
  • 409 · Ambiguous network ID. The API returns AMBIGUOUS_NETWORK_ID if a network mapping matches multiple partners. The HTML route shows a not-found page.
  • 500 · Lookup failed. API lookup failures return INTERNAL_ERROR.
  • 204 · OPTIONS. CORS preflight succeeds with no response body.

Example API error · 400

{
  "error": {
    "code": "INVALID_SEARCH_QUERY",
    "message": "Provide one domain term of 1–253 characters without control characters, page from 1 to 2147483647, and pageSize from 1 to 100. Parameters must not be repeated."
  }
}

Example API error · 404

{
  "error": {
    "code": "NOT_FOUND",
    "message": "Publisher not found."
  }
}

Example API error · 409

{
  "error": {
    "code": "AMBIGUOUS_NETWORK_ID",
    "message": "Network identifier is not unique."
  }
}

Example API error · 500

{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "Unable to load publisher."
  }
}

The database-backed API sends Access-Control-Allow-Origin: *, allows GET, HEAD and OPTIONS, and uses Cache-Control: no-store for successes and errors. These handlers do not implement rate limiting.

Read the OpenAPI specification
API documentation | Partner Passport