AdCrunch Custom conversions API
The custom conversions of an ad account. A custom conversion is configuration of the ad account, not an entity: it has no Insight, and AdCrunch stores none. An ad set can optimize for one custom conversion.
The custom conversions of an ad account. A custom conversion is configuration of the ad account, not an entity: it has no Insight, and AdCrunch stores none. An ad set can optimize for one custom conversion.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
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.curl "https://apis.io/api/v1/apis/adcrunch-dev:adcrunch-dev-custom-conversions-api"
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.
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: 3.2.0
info:
contact:
email: tech@adcrunch.dev
description: 'Every AdCrunch API, in one document. Each API also publishes its own, at `/openapi/.json`.
One origin serves the four, and the first segment of a path names the API. `/observe` reads the campaigns, the ads and the Insights of the ad accounts your organization connects. `/mutations` changes what runs on them. `/context` holds the Skills, Brands, Personas, Documents and Campaign Plans an organization writes down for its agents. `/assets` holds your images and videos, and places them in the library of an ad account.
One credential works on the four — see the security scheme.
The guide explains what these operations share: authentication, the failure shape, and the conventions for ids, money, time and pagination. It also describes three flows that span several operations: a change, an upload, and an edit that does not overwrite.
Start at https://docs.adcrunch.dev/api/introduction.'
license:
name: Proprietary
url: https://adcrunch.dev
title: AdCrunch Custom conversions API
version: 1.0.0
servers:
- description: production stage
url: https://api.adcrunch.dev
security:
- bearerAuth: []
tags:
- description: 'The custom conversions of an ad account. A custom conversion is configuration of the ad account, not an entity: it has no Insight, and AdCrunch stores none. An ad set can optimize for one custom conversion.'
name: Custom conversions
paths:
/observe/{advertiserId}/custom-conversions:
get:
security:
- bearerAuth: []
description: '**Available on:** 
Live read. Asks Meta now for the custom conversions of one ad account. A custom conversion is a rule on the events of a Pixel, such as a `StartTrial` event on a URL that contains `/welcome`. Its `id` is the `customConversionId` of an ad set that optimizes for it.
`usable` is false when Meta archived the custom conversion or made it unavailable: an ad set cannot optimize for it. `lastFiredTime` tells which custom conversion receives events. When no page holds a custom conversion, up to the page with no `nextCursor`, the ad account has none.
An advertiser that your organization does not own answers `404`, with `error` of `not_found`. An advertiser that does not exist, and one that is not a Meta ad account, answer the same.
AdCrunch stores nothing from this read. Each request counts against the rate limits of Meta.
`limit` and `cursor` page the answer. Send `nextCursor` back as `cursor` to get the next page. A page can hold no row and still carry `nextCursor`.'
x-adcrunch-providers:
- meta
operationId: listAdvertiserCustomConversions
responses:
'200':
content:
application/json:
example:
data:
- customEventType: START_TRIAL
id: '901234567890123'
lastFiredTime: 2026-09-24T07:58:40+0000
name: Trial started
pixelId: '812345678901234'
rule: '{"and":[{"event":{"eq":"StartTrial"}},{"url":{"i_contains":"/welcome"}}]}'
usable: true
- customEventType: LEAD
id: '901234567890124'
lastFiredTime: null
name: Old lead form
pixelId: '812345678901234'
rule: '{"and":[{"event":{"eq":"Lead"}}]}'
usable: false
schema:
type: object
properties:
data:
type: array
items:
type: object
properties:
customEventType:
description: 'The standard event that the custom conversion counts as, in the words of Meta: `START_TRIAL`, `LEAD`. Null when Meta sends none.'
type:
- string
- 'null'
id:
type: string
description: The Meta id of the custom conversion. It is the `customConversionId` of an ad set that optimizes for this custom conversion.
lastFiredTime:
description: The time of the last event that matched the rule, as Meta writes it (ISO 8601). Null for a custom conversion that never fired.
type:
- string
- 'null'
name:
type: string
description: The name of the custom conversion in Meta.
pixelId:
description: The Meta id of the Pixel whose events the rule reads. Null for a custom conversion of another source.
type:
- string
- 'null'
rule:
description: 'The rule of the custom conversion: the JSON of Meta, as a string. Null when Meta sends none.'
type:
- string
- 'null'
usable:
type: boolean
description: False when Meta archived the custom conversion or made it unavailable. An ad set cannot optimize for a custom conversion that is not usable.
required:
- customEventType
- id
- lastFiredTime
- name
- pixelId
- rule
- usable
additionalProperties: false
description: 'The custom conversions of the ad account, in the order of Meta. An empty array can carry `nextCursor`: then more rows can exist.'
nextCursor:
description: Send this value as `cursor` to get the next page. It is absent on the last page.
type: string
required:
- data
additionalProperties: false
example:
data:
- customEventType: START_TRIAL
id: '901234567890123'
lastFiredTime: 2026-09-24T07:58:40+0000
name: Trial started
pixelId: '812345678901234'
rule: '{"and":[{"event":{"eq":"StartTrial"}},{"url":{"i_contains":"/welcome"}}]}'
usable: true
- customEventType: LEAD
id: '901234567890124'
lastFiredTime: null
name: Old lead form
pixelId: '812345678901234'
rule: '{"and":[{"event":{"eq":"Lead"}}]}'
usable: false
description: The custom conversions of this advertiser.
'400':
content:
application/json:
schema:
anyOf:
- type: object
properties:
issues:
type: array
items:
type: object
properties:
in:
type: string
enum:
- body
- cookie
- headers
- params
- query
description: The part of the request that holds the field.
message:
type: string
description: A sentence to show a person. Written to say what to do next. Reworded whenever it can be said better, so never branch on it.
path:
type: string
description: A JSON Pointer into that part of the request, such as `/filename`. An empty string is the whole part.
required:
- in
- message
- path
additionalProperties: false
description: One entry for each field that does not match.
error:
type: string
enum:
- invalid_request
description: A stable code for the failure. This is the field to branch on. It does not change for a given failure.
message:
type: string
description: A sentence to show a person. Written to say what to do next. Reworded whenever it can be said better, so never branch on it.
required:
- issues
- error
- message
additionalProperties: false
- type: object
properties:
error:
type: string
enum:
- invalid_cursor
description: A stable code for the failure. This is the field to branch on. It does not change for a given failure.
message:
type: string
description: A sentence to show a person. Written to say what to do next. Reworded whenever it can be said better, so never branch on it.
required:
- error
- message
additionalProperties: false
description: 'The request does not match the schema of this operation: a field is missing or has the wrong type, or the body is not valid JSON. `error` is `invalid_request`, and `issues` names each field. Nothing was changed. Or AdCrunch cannot use the `cursor`: it cannot read it, or the cursor belongs to a different query. Then `error` is `invalid_cursor`. Send the `nextCursor` of the previous page with no change, or omit `cursor` to get the first page.'
'401':
content:
application/json:
schema:
type: object
properties:
error:
type: string
enum:
- unauthorized
description: A stable code for the failure. This is the field to branch on. It does not change for a given failure.
message:
type: string
description: A sentence to show a person. Written to say what to do next. Reworded whenever it can be said better, so never branch on it.
required:
- error
- message
additionalProperties: false
description: No API key, or one that does not resolve. See the security scheme. `error` is `unauthorized`.
'402':
content:
application/json:
schema:
type: object
properties:
paywallUrl:
type: string
format: uri
description: The page where an owner of the organization subscribes. Show it to the person.
error:
type: string
enum:
- organization_unentitled
description: A stable code for the failure. This is the field to branch on. It does not change for a given failure.
message:
type: string
description: A sentence to show a person. Written to say what to do next. Reworded whenever it can be said better, so never branch on it.
required:
- paywallUrl
- error
- message
additionalProperties: false
description: The organization has no active subscription. An owner subscribes at `paywallUrl`. `error` is `organization_unentitled`.
'403':
content:
application/json:
schema:
type: object
properties:
error:
type: string
enum:
- forbidden
description: A stable code for the failure. This is the field to branch on. It does not change for a given failure.
message:
type: string
description: A sentence to show a person. Written to say what to do next. Reworded whenever it can be said better, so never branch on it.
required:
- error
- message
additionalProperties: false
description: The caller does not hold `observe:read`. `error` is `forbidden`.
'404':
content:
application/json:
schema:
type: object
properties:
error:
type: string
enum:
- not_found
description: A stable code for the failure. This is the field to branch on. It does not change for a given failure.
message:
type: string
description: A sentence to show a person. Written to say what to do next. Reworded whenever it can be said better, so never branch on it.
required:
- error
- message
additionalProperties: false
description: Your organization does not own the advertiser in the path, it does not exist, or it is not a Meta ad account. The answer does not tell them apart. `error` is `not_found`. AdCrunch asks Meta nothing.
'409':
content:
application/json:
schema:
type: object
properties:
provider:
type: string
description: 'The ad platform this failure came from: `meta`.'
error:
type: string
enum:
- provider_not_connected
description: A stable code for the failure. This is the field to branch on. It does not change for a given failure.
message:
type: string
description: A sentence to show a person. Written to say what to do next. Reworded whenever it can be said better, so never branch on it.
required:
- provider
- error
- message
additionalProperties: false
description: This advertiser has no usable connection to Meta. A person must reconnect it.
'502':
content:
application/json:
schema:
type: object
properties:
platformError:
description: The provider’s own error object, unchanged. For Meta it carries `code`, `error_subcode`, `type`, `error_user_title` and `error_user_msg` — branch on the subcode for anything AdCrunch does not model. It never holds the request AdCrunch sent.
provider:
type: string
description: 'The ad platform this failure came from: `meta`.'
error:
type: string
enum:
- provider_error
description: A stable code for the failure. This is the field to branch on. It does not change for a given failure.
message:
type: string
description: A sentence to show a person. Written to say what to do next. Reworded whenever it can be said better, so never branch on it.
required:
- platformError
- provider
- error
- message
additionalProperties: false
description: The provider refused the request, or could not answer it. This failure is often transient. Send the request again.
summary: List an advertiser’s Meta custom conversions
tags:
- Custom conversions
parameters:
- name: advertiserId
in: path
required: true
schema:
type: string
pattern: ^(acc_)[\s\S]{0,}$
description: The ad account to read, prefixed `acc_`. It must belong to your organization. One that does not answers `404`, with `error` of `not_found`.
- name: cursor
in: query
required: false
schema:
description: 'The `nextCursor` of the previous page. Omit it to get the first page. Send it with no change, and with the same filters as the request that answered it: a cursor from a different query gets a 400 `invalid_cursor`. Do not build or change a cursor.'
type: string
minLength: 1
- name: limit
in: query
required: false
schema:
default: 100
description: The greatest number of rows on the page, from 1 to 500. The default is 100. A greater value gets a 400, with `error` of `invalid_request`.
type: integer
minimum: 1
maximum: 500
components:
securitySchemes:
bearerAuth:
description: 'Send `Authorization: Bearer <credential>`.
Use an **API key** (`acr_…`), from the AdCrunch console under Settings → API keys.
The credential names the organization, and no operation takes an organization parameter.
See https://docs.adcrunch.dev/api/authentication.'
scheme: bearer
type: http
x-adcrunch-commit: 7a89aff7eb17461cc80089b9374e622947e37b2b
x-tagGroups:
- name: Observe API
tags:
- Insights
- Campaigns
- Ad groups
- Ads
- Creatives
- Custom audiences
- Asset groups
- Pages
- Pixels
- Custom conversions
- Targeting options
- Stored reads
- name: Mutations API
tags:
- Mutations
- name: Context API
tags:
- Skills
- Brands
- Personas
- Documents
- Campaign Plans
- Line Items
- name: Assets API
tags:
- Assets