Reference / MCP tool reference
Reference
Count an audience
count_audience: Counts UK contacts and companies matching an audience, inline or a saved aud_ id, and returns Total, New to you and Emailable, up to three breakdowns, quality, the compliance summary, a price estimate and a count id.
Counts UK contacts and companies matching an audience, inline or a saved aud_ id, and returns Total, New to you and Emailable, up to three breakdowns, quality, the compliance summary, a price estimate and a count id. Use it whenever a filter changes, and before preview_records or quote_order, which both take the count id. Free and needs no account; anonymous and trial callers see counts under 100 and breakdown cells rounded to the nearest 5 (disclosure says which), and exact small counts need a paid plan. It returns no contact details, and over MCP a cross-tab holds at most 200 cells (truncated true; GET /v1/counts/{id} has the rest). Lawful basis is UK GDPR Article 6(1)(f) legitimate interests; email audiences include corporate subscribers only by default (PECR), so sole traders and ordinary partnerships are left out unless the toggle is changed.
| Property | Value |
|---|---|
| Tool name | count_audience |
| Position in tools/list | 8 of 22 |
| Cost | Free |
| Scope | None. Works without an account. |
| Annotations | readOnlyHint true, destructiveHint false, idempotentHint true, openWorldHint false |
| Kind | Read only; idempotent, not destructive, closed world |
| REST equivalent | POST /v1/counts |
Input
The schema sets additionalProperties to false, so a member that is not listed is refused.
| Member | Type | Required | Description |
|---|---|---|---|
audience | string or Audience | yes | An audience definition (mydatahub.audience/1) inline, or the aud_ id of a saved audience. |
level | enum: contact, company | no | contact (the default) or company. Contacts and companies come back together either way. Default contact. |
breakdown_by | array of enum: company.sic_2007, company.business_class, company.employee_band, company.turnover_band, company.fleet_band, company.type, company.status, company.subscriber_class, and 11 more | no | Up to three fields to break the count down by, each once, such as industry by seniority by region. Returns the top 50 cells per field plus other. |
Example call
The params of a tools/call request:
{
"name": "count_audience",
"arguments": {
"audience": {
"schema": "mydatahub.audience/1",
"dataset": "uk_contacts",
"where": {
"all": [
{
"field": "contact.title",
"op": "matches",
"terms": ["marketing manager"],
"mode": "contains"
},
{
"field": "contact.job_level_rank",
"op": "in",
"values": [1, 2, 3, 4, 5, 6, 8, 9]
},
{
"field": "company.sic_2007",
"op": "in",
"values": ["C"]
},
{
"field": "company.employee_band",
"op": "gte",
"value": "50-99"
},
{
"field": "site.location",
"op": "within_radius",
"centre": {
"place": "Leeds"
},
"radius_miles": 20
},
{
"not": {
"field": "email.address_sha256",
"op": "in_list",
"list_id": "sup_01JB4H7Q2V7XN3M9R6T1Y5W8ZA"
}
}
]
}
},
"level": "contact",
"breakdown_by": ["contact.job_level_rank"]
}
}Output
The result carries structuredContent that matches this schema, and one line of text that leads with the number.
| Member | Type | Always present | Description |
|---|---|---|---|
id | string | yes | Format: a count id: cnt_ followed by 20 base32 characters (A to Z and 2 to 7), such as cnt_7K2PQ4MAXN3R6TBY5WDZ. |
dataset | enum: uk_contacts, ie_contacts, global_contacts | yes | The dataset an audience draws from. uk_contacts is live; ie_contacts and global_contacts are listed but not yet served. |
dataset_version | string | yes | The immutable weekly build a count is bound to, such as 2026-W39. The last 52 stay queryable. |
engine_rules_version | string | yes | The engine rules (title expansion, defaults, geography sets) the count ran under; part of what the count id hashes, such as 2026.09.1. |
as_of | string (date) | no | The date the dataset version was built, for as-of statements. |
level | enum: contact, company | no | contact (the default) or company. Contacts and companies come back together either way. |
credits_per_record | number (0 or more) | yes | Credits, to one decimal place: whole at 1 credit a record, fractional only where a rule is (0.1 company-only, 1.8 outright, both proposed). |
contacts | integer (0 or more) | yes | Total: every matching contact. |
companies | integer (0 or more) | yes | |
emailable | integer (0 or more) | yes | A named email scoring 70 or more at a corporate subscriber. |
new_to_you | integer (0 or more) or null | yes | Excludes your earlier orders and CRM overlap; null when signed out. |
premium | integer (0 or more) or null | yes | Premium: the contacts of all_records that are premium records (contact.premium true). At launch, a premium record is in the premium export of a stated date. Null when the dataset version carries no premium data, or withheld when the premium records are at a single company and the caller has no account; premium_note says which. |
all_records | integer (0 or more) | yes | All records: every matching contact with the audience's top-level premium filters taken out (a premium filter inside any or not stays), so the premium figure always has the full figure beside it. The same as contacts when the audience has no premium filter. |
premium_note | string | yes | What premium means on this dataset version, or why premium is null: no premium data on the version, or withheld because the premium records are at a single company. |
views | CountViews | no | Contacts and companies for the three headline views, and the premium figure beside all records. |
views.total | CountView | yes | Contacts and companies in one headline view. |
views.new_to_you | CountView or null | yes | Removes records from earlier orders and a connected CRM; null when signed out. |
views.emailable | CountView | yes | A named email scoring 70 or more at a corporate subscriber. |
views.premium | CountView or null | yes | The premium records of all_records (contact.premium true); null when the dataset version carries no premium data, or withheld when they are at a single company and the caller has no account. |
views.all_records | CountView | yes | The full figure: the audience with its top-level premium filters taken out (one inside any or not stays). The same as total when it has none. |
quality | Quality | yes | Quality signals shown with every count. |
quality.email_coverage_pct | number (0 to 100) | yes | Records with a named email as a share of the count. |
quality.verified_90d_pct | number (0 to 100) | yes | Share of emails with a provider result of ok in the last 90 days. |
quality.verified_measured_at | string (date) | no | |
quality.deliverability | object | no | Score distribution: good 70 to 100, risky 40 to 69, poor under 40. |
quality.avg_days_since_verification | number (0 or more) | no | |
quality.phone_coverage_pct | number (0 to 100) | no | Share with a validated number, when phone is requested. |
quality.tps_ctps_screened_at | string (date) | no | The version's TPS and CTPS screen date, when phone is requested and a number in it is screened; each number carries its own status and screen date. |
breakdowns | array of Breakdown | yes | |
breakdowns[].by | enum: company.sic_2007, company.business_class, company.employee_band, company.turnover_band, company.fleet_band, company.type, company.status, company.subscriber_class, and 11 more | yes | A field a count can be broken down by. |
breakdowns[].cells | array of BreakdownCell | yes | |
breakdowns[].other | OtherBucket or null | no | |
crosstab | Crosstab or null | no | |
crosstab.by | array of enum: company.sic_2007, company.business_class, company.employee_band, company.turnover_band, company.fleet_band, company.type, company.status, company.subscriber_class, and 11 more | yes | |
crosstab.cells | array of CrosstabCell | yes | |
crosstab.other | OtherBucket or null | yes | |
crosstab.truncated | boolean | yes | True when a cap cut the table: 5,000 cells, or fewer over MCP to keep a result under about 8,000 tokens. |
compliance_summary | ComplianceSummary | yes | Beside every count, so a person or an agent can reason before spending. Individual subscribers and TPS and CTPS registered numbers are excluded by default, as visible toggles; numbers not screened are kept. |
compliance_summary.lawful_basis | enum: legitimate_interests_art6_1_f | yes | |
compliance_summary.corporate_subscriber | integer (0 or more) | yes | Records at corporate subscribers. |
compliance_summary.individual_subscriber_excluded | integer (0 or more) | yes | Sole traders and ordinary partnerships left out of an email audience. |
compliance_summary.tps_ctps_excluded | integer (0 or more) | yes | TPS or CTPS registered numbers left out when phone is requested. |
compliance_summary.article14_notified | integer (0 or more) | yes | |
compliance_summary.article14_pending_within_window | integer (0 or more) | yes | Records inside the one month Article 14 window, not yet notified. |
compliance_summary.suppressed_removed | array of SuppressionRemoval | yes | |
compliance_summary.jurisdiction_excluded | array of JurisdictionExclusion | no | Partner datasets only: records the jurisdiction policy table left out (Germany, Canada and California by policy), reported like suppression removals. |
defaults | array of CountDefault | yes | Every default with the number behind its toggle: dissolved and insolvent, generic emails, individual subscribers, TPS and CTPS registered. |
defaults[].id | enum: active_companies, named_emails, corporate_subscribers, no_registered_phones | yes | active_companies, named_emails, corporate_subscribers or no_registered_phones. |
defaults[].applied | boolean | yes | False when the audience filters the same field itself, or turned it off. |
defaults[].views | array of enum: total, new_to_you, emailable | yes | The headline views the default shapes; named emails and corporate subscribers shape Emailable only. |
defaults[].removed | integer (0 or more) or under 10 or suppressed | yes | What the toggle shows: the dissolved and insolvent count, the generic (info@, sales@) count, the individual subscriber count or the TPS and CTPS registered count. |
defaults[].screened_at | string (date) or null | no | no_registered_phones only: the version's TPS and CTPS screen date. |
estimate | PriceEstimate | yes | What ordering the whole count would cost. An estimate; quote_order gives the machine quote. |
estimate.credits | number (0 or more) | yes | Credits, to one decimal place: whole at 1 credit a record, fractional only where a rule is (0.1 company-only, 1.8 outright, both proposed). |
estimate.gbp_ex_vat | map of number (0 or more) | yes | The price in GBP excluding VAT: per_contact_scale (every credit at the scale) and cheapest (the cheapest real purchase, at the scale or with credit packs). |
expanded_terms | array of ExpandedTerm | no | Every title a matches filter covered, with a count per term. |
expanded_terms[].term | string | yes | A matched title. |
expanded_terms[].from_term | string | yes | The input term it came from. |
expanded_terms[].expansion_set | enum: plural_singular, department_synonyms, ampersand_and, abbreviations, permutations or input | yes | The expansion set that produced it, or input for the term itself. |
expanded_terms[].contacts | integer (0 or more) or under 10 or suppressed | yes | A whole number; "under 10" when disclosure control withholds a small cell; or "suppressed" when secondary suppression hides a cell of 10 or more in a cross-tab or breakdown, so totals cannot be differenced. Anonymous and trial callers see cells rounded to the nearest 5. |
expanded_terms[].excluded | boolean | no | True when exclude_terms removed it from the audience. |
disclosure | Disclosure | yes | Whether these numbers are exact or protected: "under 10" cells, rounding to 5 and secondary suppression for anonymous and trial callers. |
disclosure.level | enum: exact, rounded | yes | exact for authenticated paid callers; rounded for anonymous and trial callers, whose counts under 100 and every cell are rounded to the nearest 5. |
disclosure.rounded_to | integer or null | yes | 5 when numbers are rounded; null when they are exact. |
disclosure.secondary_suppression | boolean | yes | True when cross-tab or breakdown cells of 10 or more were hidden as "suppressed" so totals cannot be differenced. |
disclosure.notes | array of string | yes | |
effective_audience | Audience | no | The definition after defaults and bundles, as hashed into the id. See Audience. |
plan | CompiledPlan | no | How the definition was run: expanded terms, resolved centres, SIC codes behind a business class and suppression checksums. |
plan.engine_rules_version | string | yes | |
plan.defaults_applied | array of string | yes | Defaults the server filled, such as active_companies and named_emails. |
plan.expanded_terms | array of ExpandedTerm | yes | |
plan.centres | array of ResolvedCentre | yes | |
plan.sic_codes | array of string | yes | SIC codes behind any business class filter. |
plan.geography_sets | array of string | yes | |
plan.suppressions | array of SuppressionChecksum | yes | |
current | CurrentCount or null | no | On a replay: the same audience on the current dataset version. |
current.dataset_version | string | yes | The immutable weekly build a count is bound to, such as 2026-W39. The last 52 stay queryable. |
current.contacts | integer (0 or more) | yes | |
current.companies | integer (0 or more) | yes | |
removed_since | integer (0 or more) | no | On a replay: records since removed by objections and erasure. |
Showing the first 80 of 83 members. The OpenAPI document and the MCP tool list hold the full schema.
Errors
A failed call returns isError true. The text content leads with the sentence a person acts on, then the whole problem as JSON, with code, detail, next_step and did_you_mean. A 401 and a step-up 403 are answered at HTTP level so the client can start authorisation. See Errors.