Search: count a filter set
POSThttps://go.myleadfox.com/api/v1/search
Counts how many shops match a filter set and returns a search_id for it. It never returns rows. Use it to size a list before spending download credits, then page the rows with /rows and the search_id.
- Key scope
- search
- Spends
- search credits
- Cost
- 1 search credit per call, including when nothing matches
Request body fields
| Field | Type | Required / default | Limit | What it does |
|---|---|---|---|---|
| filters | array of filter objects | optional — defaults to [] (no filter) | at most 64 filters; a regex filter at most 25 parts of at most 200 characters each (past either: 422) | The filter expression. Each entry names a kind, a column (col) and its operator; entries join with conn (and / or) and any one can be negated with not: true. An empty list matches every shop. Every kind and operator is in the Filters section, with a runnable example each. A filter whose CONTENT is wrong (no col, an unknown or hidden column, an unknown conn, a bad value) is a 400 invalid_request; an entry that is not a JSON object at all is a 422. |
| search | string or null | optional — defaults to null | no length bound | Global text search: a case-insensitive substring match across six columns (domain, name, tagline, description_preview, emails, categories). It ANDs onto the whole filter expression. A call with non-empty search text is a "global text search", which uses more rate-limit units (see Limits). |
| search_id | string or null | optional — defaults to null | wins over filters and search when both are sent | A saved search: a 16-character id. It is the id in a grid URL — https://go.myleadfox.com/grid/<search_id> — so you can copy it from any grid link (your own, a teammate's, a shared one), or take it from a /search response. It never expires. It stores the filters and the search text, not a snapshot of rows, so every call runs live against today's data. If you send search_id AND filters or search, the search_id is used and the others are ignored. An id that does not resolve is a 404 search_not_found. |
| sort | array of {col, dir} objects | optional — defaults to [] (unsorted) | at most 8 keys (more: 422) | Ordering, most significant key first: [{"col": "products", "dir": "desc"}]. dir is asc or desc (any case) and defaults to asc. An unknown or hidden column is a 400 unknown_column; an entry with no col, or a dir that is not asc/desc, is a 400 invalid_request; an entry that is not a JSON object is a 422. Sort is NOT stored in a search_id — send it again on every call that needs it. |
Ignored here: page, limit, columns and include_total are not /search fields: they are silently ignored here. To get rows, call /rows.
Example
curl -X POST https://go.myleadfox.com/api/v1/search \
-H 'Authorization: Bearer mlf_live_YOUR_KEY' \
-H 'Content-Type: application/json' \
-d '{
"filters": [
{ "kind": "dropdown", "col": "country", "values": ["US"] }
]
}'{
"search_id": "de11cac33c3da5df",
"search_url": "/grid/de11cac33c3da5df",
"total": 1281396,
"pages": 12814,
"page_size": 100,
"filters": [
{
"kind": "dropdown",
"col": "country",
"values": [
"US"
]
}
],
"search": null,
"sort": [],
"global_search": false,
"credits": {
"search_remaining": 4999,
"download_remaining": 200000,
"enrich_remaining": 500,
"charged": 1,
"charged_unit": "searches"
}
}Every response field
| Field | Type | Meaning |
|---|---|---|
| search_id | string | The id of this search (16 characters). It is a content hash: the same filters and search text always give the same id. Send it to /rows to page the result without re-sending the filters, or open it in the app at https://go.myleadfox.com/grid/<search_id>. It never expires. |
| search_url | string | The same search as a path in the app: /grid/<search_id>. It is relative — put https://go.myleadfox.com in front of it. |
| total | integer | How many shops match: an exact count (not an estimate), counted live at the moment of the call. |
| pages | integer | How many 100-row pages that total makes (total ÷ 100, rounded up). 0 when total is 0. |
| page_size | integer | Always 100. |
| filters | array | The filters that actually ran — the stored ones when you sent a search_id. Compare with what you sent when a count surprises you. |
| search | string or null | The global search text that ran, or null. |
| sort | array | Your sort, after validation. It is not stored in the search_id. |
| global_search | boolean | true when search text was applied (a global text search). |
| credits | object | The credits block — see "What each call uses". Here charged is 1 and charged_unit is "searches". |
Good to know
- An empty body {} is valid: it counts every shop (and costs 1 search credit).
- The search credit is taken before the count runs. If the count then fails on our side, the credit is refunded and the reply is 500 server_error. A refusal (400, 404, 422) costs nothing.
- A search that matches nothing still costs 1 search credit: the query ran. total and pages are 0 (see the example below).
More real responses
A global text search
search text instead of filters. global_search comes back true.
curl -X POST https://go.myleadfox.com/api/v1/search \
-H 'Authorization: Bearer mlf_live_YOUR_KEY' \
-H 'Content-Type: application/json' \
-d '{
"search": "coffee"
}'{
"search_id": "50f15173380e2c18",
"search_url": "/grid/50f15173380e2c18",
"total": 65046,
"pages": 651,
"page_size": 100,
"filters": [],
"search": "coffee",
"sort": [],
"global_search": true,
"credits": {
"search_remaining": 4998,
"download_remaining": 200000,
"enrich_remaining": 500,
"charged": 1,
"charged_unit": "searches"
}
}Zero matches still costs a credit
The query ran, so credits.charged is 1. total and pages are 0. Asking /rows for page 1 of this search returns 200 with rows [] and charges nothing; page 2 or later is a 404 page_out_of_range.
curl -X POST https://go.myleadfox.com/api/v1/search \
-H 'Authorization: Bearer mlf_live_YOUR_KEY' \
-H 'Content-Type: application/json' \
-d '{
"filters": [
{ "kind": "text", "col": "domain", "mode": "exact", "value": "no-such-domain.invalid" }
]
}'{
"search_id": "739b3ff5025d5917",
"search_url": "/grid/739b3ff5025d5917",
"total": 0,
"pages": 0,
"page_size": 100,
"filters": [
{
"kind": "text",
"col": "domain",
"mode": "exact",
"value": "no-such-domain.invalid"
}
],
"search": null,
"sort": [],
"global_search": false,
"credits": {
"search_remaining": 4997,
"download_remaining": 200000,
"enrich_remaining": 500,
"charged": 1,
"charged_unit": "searches"
}
}What it refuses
| Response | When |
|---|---|
| insufficient_credits · 402 | No search credit left (pool "search", needed 1). Nothing is counted. |
| search_not_found · 404 | The search_id does not resolve. The response repeats it in a search_id field. |
| invalid_request · 400 | A filter whose content is wrong (no col, an unknown or hidden column, an unknown conn, a bad value), a sort entry with no col, or a dir that is not asc/desc. |
| unknown_column · 400 | A sort key names an unknown or hidden column. The name comes back in a column field. |
| server_error · 500 | The count failed on our side. The search credit is refunded. |
| 422 | A filter or sort entry that is not a JSON object, more than 64 filters, more than 8 sort keys, or a regex part past its bounds. |
Plus the key and account refusals every call can get — see Errors. A 422 is a schema error with a different body shape.
