Logo.dev Transaction Enrichment API

Turn a card transaction descriptor into a merchant identity. Private beta.

Operations 1

POST /transaction Identify a merchant #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/logo-dev:logo-dev-transaction-enrichment-api-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

logo-dev-transaction-enrichment-api-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Logo.dev Transaction Enrichment API
  description: 'Logo.dev serves brand assets from two hosts, each with its own key:


    - **Logo.dev CDN** (`img.logo.dev`) returns a logo image. Authenticate with a **publishable key** in the `token` query parameter. It is safe in client-side code.

    - **Logo.dev API** (`api.logo.dev`) returns JSON. Authenticate with a **secret key** in the `Authorization: Bearer` header. Call it only from your server.


    Get your keys from the dashboard.'
  version: 2.0.0
  contact:
    name: Logo.dev Support
    email: support@logo.dev
    url: https://docs.logo.dev
servers:
- url: https://api.logo.dev
  description: REST APIs (Brand Search, Brand Data)
tags:
- name: Transaction Enrichment API
  description: Turn a card transaction descriptor into a merchant identity. Private beta.
paths:
  /transaction:
    servers:
    - url: https://www.logo.dev/api
      description: Transaction beta on Vercel
    post:
      tags:
      - Transaction Enrichment API
      summary: Identify a merchant
      description: 'Identify the merchant behind a merchant name or card descriptor. Every non-empty `transaction` returns one row: `matched` with a merchant domain, or `cleaned` with a display name and no domain.


        Transaction is a private beta with its own host and its own `tx_beta_` key. Errors use a single `error` string, not the Logo.dev API error object. Read the Transaction guide for access, the daily allowance, and how to act on a row. Descriptor matching is experimental.'
      operationId: identifyMerchant
      x-mint:
        metadata:
          description: Identify a merchant domain and brand record. Private beta with a daily allowance.
      security:
      - transactionBetaKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required:
              - transaction
              properties:
                transaction:
                  type: string
                  minLength: 1
                  maxLength: 1024
                  description: The merchant name or card descriptor. The API trims surrounding whitespace, then the value must be 1 to 1,024 characters.
                  examples:
                  - Starbucks
                country_code:
                  type: string
                  minLength: 2
                  maxLength: 2
                  description: ISO 3166-1 alpha-2 country code, such as `US` or `GB`. Lowercase is accepted. The code is a hint for ambiguous names. It does not restrict results to that country. An unknown code returns `400`. With `transaction_context`, the codes `AQ`, `AX`, `TF`, and `VA` return `400`.
                  examples:
                  - US
                transaction_context:
                  type: object
                  additionalProperties: false
                  description: 'The real transaction values. Send them to identify the other party as a person or an organization, and to name the platform the purchase ran through. With context, the response adds `entity_type`.


                    When you send the object, `date`, `amount`, `currency`, and `entry_type` are required. The API fills in no placeholder values. Incomplete context returns `400` and does not count against the daily allowance.


                    The merchant string, the context, and the account holder details go to Logo.dev''s enrichment provider. Each lookup carries its own context. The beta keeps no account history across lookups.'
                  required:
                  - date
                  - amount
                  - currency
                  - entry_type
                  properties:
                    date:
                      type: string
                      format: date
                      description: The posted date as `YYYY-MM-DD`, from `1970-01-01` to tomorrow in UTC.
                      examples:
                      - '2026-09-04'
                    amount:
                      type: number
                      minimum: 0
                      maximum: 1000000000000
                      description: A nonnegative amount in the currency's major units, such as dollars, up to 1,000,000,000,000.
                      examples:
                      - 10
                    currency:
                      type: string
                      description: 'An ISO 4217 code that the beta supports. Lowercase is accepted. An unsupported code returns `400`. Supported codes: EUR, AED, AFN, XCD, ALL, AMD, AOA, ARS, USD, AUD, AWG, AZN, BAM, BBD, BDT, XOF, BGN, BHD, BIF, BMD, BND, BOB, BRL, BSD, INR, NOK, BWP, BYR, BZD, CAD, CDF, XAF, CHF, NZD, CLP, CNY, COP, CRC, CUP, CVE, ANG, CZK, DJF, DKK, DOP, DZD, EGP, MAD, ERN, ETB, FJD, FKP, GBP, GEL, GHS, GIP, GMD, GNF, GTQ, GYD, HKD, HNL, HUF, IDR, ILS, IQD, IRR, ISK, JMD, JOD, JPY, KES, KGS, KHR, KMF, KPW, KRW, KWD, KYD, KZT, LAK, LBP, LKR, LRD, ZAR, LYD, MDL, MGA, MKD, MMK, MNT, MOP, MRO, MUR, MVR, MWK, MXN, MYR, MZN, XPF, NGN, NIO, NPR, OMR, PEN, PGK, PHP, PKR, PLN, PYG, QAR, RON, RSD, RUB, RWF, SAR, SBD, SCR, SDG, SEK, SGD, SHP, SLL, SOS, SRD, SSP, STD, SYP, SZL, THB, TJS, TMT, TND, TOP, TRY, TTD, TWD, TZS, UAH, UGX, UYU, UZS, VEF, VND, VUV, WST, YER, ZMW, ZWL, HRK.'
                      examples:
                      - USD
                    entry_type:
                      type: string
                      enum:
                      - incoming
                      - outgoing
                      description: The direction of the money, from the bank account owner's side.
                    account_holder:
                      type: object
                      additionalProperties: false
                      required:
                      - type
                      description: The bank account owner. Send it when the account holder matters, for example to tell a person from a business.
                      properties:
                        type:
                          type: string
                          enum:
                          - consumer
                          - business
                          description: Whether the bank account owner is a consumer or a business.
                        name:
                          type: string
                          minLength: 1
                          maxLength: 1024
                          description: The bank account owner's name. The API trims surrounding whitespace. Send it when a descriptor holds several names and the owner's name helps pick out the other party.
              description: The body takes no fields other than these. An unrecognized field returns `400`.
            examples:
              merchant:
                value:
                  transaction: Starbucks
                  country_code: US
              context:
                value:
                  transaction: SQ* STARBUCKS 10 Union Sq
                  country_code: US
                  transaction_context:
                    date: '2026-09-04'
                    amount: 10
                    currency: USD
                    entry_type: outgoing
                    account_holder:
                      type: consumer
      responses:
        '200':
          description: 'A `matched` row or a `cleaned` row. Every non-empty `transaction` returns one. The examples are illustrative: they show the shape, not the result for a particular string.'
          headers:
            X-RateLimit-Limit:
              description: Your daily request limit. See [daily allowance](/transaction/introduction#daily-allowance).
              schema:
                type: integer
            X-RateLimit-Remaining:
              description: Requests remaining after this call.
              schema:
                type: integer
            X-RateLimit-Reset:
              description: Next midnight UTC as Unix seconds.
              schema:
                type: integer
          content:
            application/json:
              schema:
                type: object
                required:
                - domain
                - name
                - entity_id
                - logo_url
                - parent
                - intermediary
                - status
                - match_type
                - confidence
                properties:
                  domain:
                    type:
                    - string
                    - 'null'
                    description: The merchant's domain. `null` on a `cleaned` row.
                  name:
                    type: string
                    description: 'The display name. Present for every non-empty `transaction`.


                      - When Brand resolves a brand for the domain, its name wins.

                      - Otherwise it is the name the transaction lookup returned.

                      - On a `cleaned` row it is the descriptor, tidied into a display name.


                      Descriptors arrive in one case, so a tidied name reads `Fedex`, not `FedEx`. When the Logo.dev brand corpus holds the same name, a `cleaned` row takes the company''s own spelling. Only the name changes. The row stays `cleaned` at the same `confidence`.'
                    examples:
                    - Starbucks
                  entity_type:
                    type:
                    - string
                    - 'null'
                    enum:
                    - person
                    - organization
                    - null
                    description: 'Present only when the request includes `transaction_context`. `person` or `organization`, as the enrichment provider classified the other party, or `null` when it classified nobody.


                      A `null` does not mean the API found no merchant. A later stage can identify one, so a `matched` row can carry `null` here. A person always comes back as a `cleaned` row.


                      Without context, the API does not detect people. It treats a personal name as a merchant name, so `Jane Doe` can match an organization with a similar name.'
                  entity_id:
                    type:
                    - string
                    - 'null'
                    description: 'The merchant''s Logo.dev brand id, the same id Brand uses. A brand keeps its id when its domain changes, so key your records on `entity_id`, not `domain`.


                      A row with an id has two sources that agree: the pipeline identified the merchant, and Brand resolves a brand for the same domain. Branch on `entity_id`, not on `confidence`.


                      `null` on a `cleaned` row, and on a `matched` row when Brand resolves no brand for the domain. A domain that Logo.dev has not indexed yet also returns `null`. A later lookup can return an id after the first crawl finishes.'
                    examples:
                    - brand_5k2mq8h0000g00400000000017
                  logo_url:
                    type:
                    - string
                    - 'null'
                    format: uri
                    description: 'The Logo.dev CDN URL for the merchant''s logo. `null` on a `cleaned` row. The URL has no token. Add your publishable key before you show it: `https://img.logo.dev/starbucks.com?token=LOGO_DEV_PUBLISHABLE_KEY`. The same applies to `logo_url` in `parent` and `intermediary`.'
                    examples:
                    - https://img.logo.dev/starbucks.com
                  parent:
                    type:
                    - object
                    - 'null'
                    description: 'The company that owns the merchant, or `null`. It sits beside the merchant and never replaces it: a lookup for Facebook returns Facebook in `domain`, with Meta here.


                      Group totals on `parent.entity_id` to add up one company''s brands. Facebook, Instagram, and WhatsApp become Meta. The owner''s `entity_id` is the same id it has when it is the merchant, so the two join on one column.


                      `parent` is the nearest owner that Logo.dev records, not the top of the chain. Jaguar returns JLR, and JLR returns Tata Motors. To find the top, look up each parent in turn.


                      Coverage is thin. Logo.dev records a small, hand-verified list of ownership pairs, so most rows return `null`. A `null` means Logo.dev records no owner for this merchant. It does not mean the merchant is independent. `parent` is also `null` on a `cleaned` row, and when Brand resolves no brand for the owner''s domain.


                      A rebrand onto a new domain can come back as a new identity. `parent` joins it to the old one only when both domains are in the ownership list.'
                    required:
                    - domain
                    - name
                    - entity_id
                    - logo_url
                    properties:
                      domain:
                        type:
                        - string
                        - 'null'
                        description: The owner's domain.
                        examples:
                        - meta.com
                      name:
                        type:
                        - string
                        - 'null'
                        description: The owner's display name, from Brand.
                        examples:
                        - Meta
                      entity_id:
                        type:
                        - string
                        - 'null'
                        description: The owner's Logo.dev brand id, the same id Brand uses.
                        examples:
                        - brand_9r3hx5f0000g00400000000021
                      logo_url:
                        type:
                        - string
                        - 'null'
                        description: The Logo.dev CDN URL for the owner's logo, with no token.
                        examples:
                        - https://img.logo.dev/meta.com
                  intermediary:
                    type:
                    - object
                    - 'null'
                    description: 'The platform the purchase ran through, or `null`. The merchant stays in `domain`. A coffee paid through Square returns the coffee shop, with Square here. The platform gets the same lookup as the merchant, keyed by its own domain, so you can group totals by platform on `intermediary.entity_id`.


                      `intermediary` is `null` in these cases:


                      - The platform sold the item itself. A DashPass subscription is a purchase from DoorDash, so that row names DoorDash in `domain`.

                      - The API identified no platform. This does not mean no platform was involved.


                      Without `transaction_context`, the API fills `intermediary` only when the enrichment provider answers the whole descriptor with a platform. The merchant checks then refuse the platform as the seller, and the API looks up the text after the prefix on its own. When the provider resolves the seller directly from a prefixed descriptor, `intermediary` stays `null` on a correct row. Send `transaction_context` when you need the platform named reliably.


                      A `cleaned` row can keep a platform. This happens when the checks reject the platform''s domain and the text after the prefix identifies nobody.'
                    required:
                    - domain
                    - name
                    - entity_id
                    - logo_url
                    properties:
                      domain:
                        type:
                        - string
                        - 'null'
                        description: The platform's domain, or `null`.
                        examples:
                        - squareup.com
                      name:
                        type:
                        - string
                        - 'null'
                        description: The platform's display name, or `null`. Brand's name wins when there is one.
                        examples:
                        - Square
                      entity_id:
                        type:
                        - string
                        - 'null'
                        description: The platform's Logo.dev brand id, or `null` when Brand resolves no brand for its domain.
                        examples:
                        - brand_7c1pz4d0000g00400000000052
                      logo_url:
                        type:
                        - string
                        - 'null'
                        description: The Logo.dev CDN URL for the platform's logo, with no token, or `null` when it has no domain.
                        examples:
                        - https://img.logo.dev/squareup.com
                  status:
                    type: string
                    enum:
                    - matched
                    - cleaned
                    description: '`matched` when the API identified a merchant domain. `cleaned` when it did not. There is no not-found result: a `cleaned` row is an answer, with a tidied display name and `null` in `domain`, `entity_id`, `logo_url`, and `parent`.


                      A row is `cleaned` for one of two reasons:


                      - Nothing identified the descriptor.

                      - A candidate domain failed a merchant check. The checks catch repeated hostnames, directory and profile hosts, a known platform in the merchant''s place, and known descriptor conflicts. They run on saved results and on new lookups.


                      A failed check drops the domain and uses the tidied descriptor as the name. A platform that a check removes from the merchant''s place still comes back in `intermediary`. An organization with no usable website also comes back `cleaned`. The API never puts a payment processor''s or parent company''s domain in place of the merchant''s.'
                  match_type:
                    type: string
                    enum:
                    - enriched_context
                    - descriptor_domain
                    - resolved_entity
                    - searched_name
                    - cleaned_descriptor
                    description: 'The pipeline stage that produced the row, strongest first. The set is closed.


                      - `enriched_context`: identified from the full transaction, with entry type and account holder.

                      - `descriptor_domain`: the descriptor printed the merchant''s own domain.

                      - `resolved_entity`: identified from the descriptor alone, with no transaction context.

                      - `searched_name`: the Logo.dev brand corpus recognized the cleaned name.

                      - `cleaned_descriptor`: nothing identified the merchant. `name` holds the tidied descriptor.


                      `descriptor_domain` and `searched_name` are fallbacks. They run only when the enrichment provider identifies nobody. A descriptor that the provider recognizes never reaches them: `CHEWY.COM XXX-XXX-7111 FL` prints a domain and still returns `resolved_entity`. Both fallbacks face the same merchant checks, so a row that fails them comes back `cleaned`.


                      Read `match_type` from the row. Do not predict it from the descriptor, because the same string can take a different path as the provider''s coverage changes.'
                  confidence:
                    type: number
                    minimum: 0
                    maximum: 1
                    description: 'A score from 0 to 1. Two inputs set it: the `match_type` that produced the row, and whether Brand resolves a brand for the same domain. A printed domain scores above a name resolved from the descriptor, and a corpus search scores below it. A failed merchant check sends the row to the floor. A `matched` row with no brand has a lower score and a `null` `entity_id`.


                      The same input always gets the same score, so two runs of a batch compare cleanly.


                      Logo.dev publishes no cut-off, because the right line depends on what the row feeds. Use the score to rank and triage, for example to sort a review queue. To branch, read `entity_id`. Logo.dev can recalibrate the score without changing which rows have an id. A high score does not guarantee accuracy.'
              examples:
                matched:
                  value:
                    domain: starbucks.com
                    name: Starbucks
                    entity_id: brand_5k2mq8h0000g00400000000017
                    logo_url: https://img.logo.dev/starbucks.com
                    parent: null
                    intermediary: null
                    status: matched
                    match_type: resolved_entity
                    confidence: 0.8
                matchedWithContext:
                  value:
                    domain: starbucks.com
                    name: Starbucks
                    entity_type: organization
                    entity_id: brand_5k2mq8h0000g00400000000017
                    logo_url: https://img.logo.dev/starbucks.com
                    parent: null
                    intermediary:
                      domain: squareup.com
                      name: Square
                      entity_id: brand_7c1pz4d0000g00400000000052
                      logo_url: https://img.logo.dev/squareup.com
                    status: matched
                    match_type: enriched_context
                    confidence: 0.95
                ownedByAParent:
                  value:
                    domain: facebook.com
                    name: Facebook
                    entity_id: brand_2n8vt6b0000g00400000000038
                    logo_url: https://img.logo.dev/facebook.com
                    parent:
                      domain: meta.com
                      name: Meta
                      entity_id: brand_9r3hx5f0000g00400000000021
                      logo_url: https://img.logo.dev/meta.com
                    intermediary: null
                    status: matched
                    match_type: resolved_entity
                    confidence: 0.8
                uncorroborated:
                  value:
                    domain: bluedoorpub.com
                    name: Blue Door Pub
                    entity_id: null
                    logo_url: https://img.logo.dev/bluedoorpub.com
                    parent: null
                    intermediary: null
                    status: matched
                    match_type: resolved_entity
                    confidence: 0.55
                cleaned:
                  value:
                    domain: null
                    name: Jane Doe
                    entity_id: null
                    logo_url: null
                    parent: null
                    intermediary: null
                    status: cleaned
                    match_type: cleaned_descriptor
                    confidence: 0.1
                cleanedWithPlatform:
                  value:
                    domain: null
                    name: Xqzv Deli
                    entity_type: organization
                    entity_id: null
                    logo_url: null
                    parent: null
                    intermediary:
                      domain: squareup.com
                      name: Square
                      entity_id: brand_7c1pz4d0000g00400000000052
                      logo_url: https://img.logo.dev/squareup.com
                    status: cleaned
                    match_type: cleaned_descriptor
                    confidence: 0.1
                person:
                  value:
                    domain: null
                    name: Jane Doe
                    entity_type: person
                    entity_id: null
                    logo_url: null
                    parent: null
                    intermediary: null
                    status: cleaned
                    match_type: cleaned_descriptor
                    confidence: 0.1
        '400':
          description: 'The request failed validation: invalid JSON, an empty or oversized `transaction`, an unknown `country_code`, invalid or incomplete `transaction_context`, or an unknown field. It does not count against the daily allowance.'
          content:
            application/json:
              schema:
                type: object
                required:
                - error
                properties:
                  error:
                    type: string
              examples:
                invalidInput:
                  value:
                    error: Provide transaction (1–1,024 characters), an optional country_code, and optional transaction_context. Context requires a valid date, nonnegative amount, supported currency, and incoming/outgoing entry_type. Account-holder details require consumer/business type. Unknown fields are rejected.
                invalidJson:
                  value:
                    error: Invalid JSON
        '401':
          description: The `Authorization` header is missing, or the beta key is malformed, unknown, or replaced by a newer key.
          content:
            application/json:
              schema:
                type: object
                required:
                - error
                properties:
                  error:
                    type: string
              examples:
                malformedKey:
                  value:
                    error: Invalid transaction beta key
                unknownKey:
                  value:
                    error: Unauthorized
        '403':
          description: The beta is not on for this account. Access belongs to a Logo.dev account, so this applies to everyone on it.
          content:
            application/json:
              schema:
                type: object
                required:
                - error
                properties:
                  error:
                    type: string
              examples:
                noAccess:
                  value:
                    error: Transaction beta access is not enabled
        '409':
          description: Another request is already looking up the same transaction. Wait for `Retry-After` and send the request again to read the saved result. The original request, the `409`, and the retry all count against the daily allowance.
          content:
            application/json:
              schema:
                type: object
                required:
                - error
                properties:
                  error:
                    type: string
              examples:
                inProgress:
                  value:
                    error: This transaction is already being looked up. Retry shortly.
          headers:
            X-RateLimit-Limit:
              description: Your daily request limit. See [daily allowance](/transaction/introduction#daily-allowance).
              schema:
                type: integer
            X-RateLimit-Remaining:
              description: Requests remaining after this call.
              schema:
                type: integer
            X-RateLimit-Reset:
              description: Next midnight UTC as Unix seconds.
              schema:
                type: integer
            Retry-After:
              description: Seconds to wait before you retry. Always `2`.
              schema:
                type: integer
        '413':
          description: The request body is larger than 8,192 bytes.
          content:
            application/json:
              schema:
                type: object
                required:
                - error
                properties:
                  error:
                    type: string
              examples:
                tooLarge:
                  value:
                    error: Request body too large
        '415':
          description: The `Content-Type` is not `application/json`.
          content:
            application/json:
              schema:
                type: object
                required:
                - error
                properties:
                  error:
                    type: string
              examples:
                wrongType:
                  value:
                    error: 'Use Content-Type: application/json'
        '429':
          description: 'A rate limit applies. Wait for `Retry-After` before you send the request again.


            - The daily allowance is spent. The response has the usage headers, and `Retry-After` counts the seconds to the next midnight UTC.

            - More than 60 requests in one minute. The response has `Retry-After` and no usage headers.'
          content:
            application/json:
              schema:
                type: object
                required:
                - error
                properties:
                  error:
                    type: string
              examples:
                dailyLimit:
                  value:
                    error: Daily limit of beta requests reached
                burstLimit:
                  value:
                    error: Too many requests
          headers:
            X-RateLimit-Limit:
              description: Your daily request limit. See [daily allowance](/transaction/introduction#daily-allowance).
              schema:
                type: integer
            X-RateLimit-Remaining:
              description: Requests remaining after this call.
              schema:
                type: integer
            X-RateLimit-Reset:
              description: Next midnight UTC as Unix seconds.
              schema:
                type: integer
            Retry-After:
              description: Seconds to wait before you retry.
              schema:
                type: integer
        '503':
          description: Enrichment, storage, or the access check is unavailable. The API returns this error, not a `cleaned` row. A failed lookup still counts against the daily allowance.
          content:
            application/json:
              schema:
                type: object
                required:
                - error
                properties:
                  error:
                    type: string
              examples:
                unavailable:
                  value:
                    error: Transaction is temporarily unavailable
          headers:
            X-RateLimit-Limit:
              description: Your daily request limit. See [daily allowance](/transaction/introduction#daily-allowance).
              schema:
                type: integer
            X-RateLimit-Remaining:
              description: Requests remaining after this call.
              schema:
                type: integer
            X-RateLimit-Reset:
              description: Next midnight UTC as Unix seconds.
              schema:
                type: integer
components:
  securitySchemes:
    publishableKey:
      type: apiKey
      in: query
      name: token
      description: Your publishable key (`pk_`). Safe in client-side code. Works only with the Logo.dev CDN.
    secretKey:
      type: http
      scheme: bearer
      description: Your secret key (`sk_`), as `Bearer sk_...`. Call the Logo.dev API only from your server.
    transactionBetaKey:
      type: http
      scheme: bearer
      bearerFormat: tx_beta_…
      description: Transaction-only beta key, issued with your beta access and always visible in the Transaction Playground. Existing Logo.dev secret keys do not authenticate this beta.