API reference
Search
POST /api/v1/engine/search: one lead in, the offers it qualifies for out.
http
POST /api/v1/engine/searchSearches every active offer on your campaign for one lead. ICON stores the lead, runs its duplicate, cap, eligibility and requirement checks, and answers with a search id (icon_lead_id) and the first results. Partner offers keep answering after that: poll Results until processing_done is true.
Rate limit: 60 per minute per key (bursts of up to 10).
#Request
| Field | Type | Required | Description |
|---|---|---|---|
icon_campaign_id | string | yes | ICON campaign ID (UUID) of the campaign this request is for. Required on every search and every submit: ICON never picks a campaign for you. |
icon_affiliate_id | string | yes | Your ICON affiliate ID (UUID). Required on every search and every submit, and must be the affiliate your API key belongs to. ICON records the affiliate from the key; this value is only checked against it. |
lead | object | yes | The consumer: lead.personal, lead.address, lead.education, lead.background. See the field dictionary. Flat keys (email, phone, zip, …) are accepted too. |
tracking.ip_address | string | yes | The consumer's own public IP address (not your server's). See The consumer's IP. |
tracking | object | — | Also your sub ids, UTM values and click ids. See Attribution. |
tcpa_consent | boolean | — | The consumer's consent decision, when you collected it before the search. See Consent and TCPA. |
trusted_form_cert_url, universal_leadid | string | — | Consent certificates, when you have them. |
icon_is_test_lead | boolean | — | Test mode: held before any buyer receives it (TEST_LEAD_HELD). |
bash
curl -X POST 'https://iconroute.io/api/v1/engine/search' \
-H "Authorization: Bearer $ICON_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"icon_campaign_id": "6f1c2d3e-4a5b-4c6d-8e9f-0a1b2c3d4e5f",
"icon_affiliate_id": "3c9d8e7f-1a2b-4c3d-9e8f-7a6b5c4d3e2f",
"lead": {
"personal": {
"first_name": "Ada",
"last_name": "Lovelace",
"email": "ada.lovelace@mail.test",
"phone": "6025552368",
"date_of_birth": "1990-04-07"
},
"address": {
"address_line_1": "123 N Main St",
"city": "Phoenix",
"state": "AZ",
"zip_code": "85004"
},
"education": {
"education_level": "bachelors",
"high_school_graduation_year": "2008",
"start_timeline": "1_3_months",
"learning_preference": "online"
},
"background": {
"military_affiliation": "none",
"us_citizen": "yes"
}
},
"tracking": {
"ip_address": "203.0.113.9",
"subid": "pub-42",
"subid2": "creative-7",
"utm_source": "partner",
"utm_medium": "email",
"utm_campaign": "fall-enrollment"
},
"tcpa_consent": true,
"trusted_form_cert_url": "https://cert.trustedform.com/0123456789abcdef0123456789abcdef01234567"
}'#Response
200 with the search id and the first results:
json
{
"code": "OK",
"status": "accepted",
"reason": "OK",
"success": true,
"icon_lead_id": "1e8d30bb-7c5f-41b9-8cf1-5e6b57abe999",
"processing_done": false,
"poll_after_ms": 1000,
"accepted_count": 0,
"max_accepted_submissions": 3,
"true_exclusive_accepted": false,
"offers": [
{
"icon_result_id": "api-0b7e4a2c-981234",
"campaign_id": "6f1c2d3e-4a5b-4c6d-8e9f-0a1b2c3d4e5f",
"offer": {
"id": "0b7e4a2c-9d8f-4e1a-b2c3-d4e5f6a7b8c9",
"name": "Example University",
"offer_type": "shared",
"is_exclusive": false,
"tcpa_text": "By clicking Submit, I agree…"
},
"school": {
"name": "Example University",
"logo_url": "https://cdn.example.com/logo.png"
},
"programs": [
{
"value": "4411",
"label": "BS Business (Online)",
"payout": 28
}
],
"payout": 28,
"form_fields": [
{
"name": "api_field_military",
"label": "Military affiliation",
"type": "select",
"required": true,
"options": [
{
"value": "none",
"label": "None"
}
]
},
{
"name": "api_field_rn_license",
"label": "RN license?",
"type": "select",
"required": false,
"required_when": {
"program_ids": [
"4411"
]
}
}
],
"expires_at": "2026-09-25T20:15:00Z"
}
]
}| Field | Type | Required | Description |
|---|---|---|---|
code | string | — | — |
status | string | — | The outcome, from one lower-case set. One of: accepted, saved, pending, held, rejected, invalid, failed. |
reason | string | — | A short sentence-case explanation, e.g. "Missing HS grad year" or "Duplicate - Client" (" - Client": the school's own system decided). |
processing_done | boolean | — | False while API offers are still answering: poll /engine/results again. |
poll_after_ms | integer | — | Present while processing_done is false: wait this long before the next poll. |
icon_lead_id | string (uuid) | — | Your search id: poll with it, and send it as search_lead_id on submit. |
accepted_count | integer | — | — |
max_accepted_submissions | integer | — | — |
true_exclusive_accepted | boolean | — | — |
offers is a list of result entries, best first. ICON orders the list; keep its order when you show it.
#Errors
| HTTP | Meaning |
|---|---|
| 400 | REQUIRED_FIELD_MISSING / INVALID_FIELD (the field is named), or NOT_FOUND: the campaign is not active or not yours. |
| 401 | AUTH_REQUIRED (no API key) or AUTH_INVALID (a key ICON does not know); status invalid. |
| 403 | FORBIDDEN: icon_affiliate_id is not the affiliate of your key, or the lead was already submitted. |
| 429 | RATE_LIMITED (retryable): this API key is over its rate limit. Wait the Retry-After header's seconds (also retry_after_seconds) and retry. Limits are per API key and per leg: search 60 per minute with bursts of up to 10, results polling 600 per minute, submit 120 per minute, unless ICON set other limits for your key. |
| 500 | INTERNAL_ERROR: not retryable as is; tell your ICON contact the time and the icon_lead_id. |
| 503 | UNAVAILABLE: retry later. |