Skip to main content

Political Planner

Plan, activate, and convert a Political proposal into an insertion order (IO) and campaigns with this in-depth tutorial using IQM's Planner API.

The following endpoints are used to create a plan, activate a proposal, and generate an IO with campaigns:

POST
POST
GET
POST
GET
POST

About Political proposals​

A Political proposal is a forecast-backed media plan that IQM can turn into a live IO and campaigns. Proposals are built in stages, and each stage produces the ID that the next one needs:

Plan → Strategy → Proposal → Insertion order and campaigns

  • A plan holds the budget, flight dates, and targeting. A plan is saved as either a draft or ready plan. Only ready plans produce strategies.
  • When a ready plan is created, IQM's forecasting service generates up to three strategies for it: Maximum Reach, Balanced, and Most Economical. Each strategy is a different trade-off between reach, impressions, and cost.
  • Activating a strategy turns it into a proposal: an editable set of proposed campaigns with its own status.
  • Generating an IO from a ready proposal creates a real IO and campaigns in your Workspace. This is a one-way step.

More resources​

Before you begin​

To create a Political proposal and convert it into an IO, the following are required:

  • An account on the IQM platform in a Political Workspace
    • See the Getting started section to create an account and request a Client ID and Client Secret.
    • Planner API requests from a Workspace outside the Political vertical are rejected.
  • A Political Advertiser profile

Log in​

POST

To log in, send the Authorization: Basic header with your Base64-encoded Client ID and Client Secret, and send the request body as form data (application/x-www-form-urlencoded). This endpoint returns an OAuth-compliant response with an access token and an Organization Workspace ID (owId), a unique identifier for each Organization. This ID is used for any further API communications.

For further information, see OAuth token.

Headers
Authorization
string required
Basic followed by your Base64-encoded client_id:client_secret.
See Sign up and authenticate.
Request schema
grant_type
string required
password to log in.
See OAuth grant types.
username
string required
User account email.
password
string required
User account password.
Request sample
curl -i --location 'https://api.iqm.com/api/v3/ua/oauth/token' \
--header 'accept: application/json, text/plain, */*' \
--header 'Authorization: Basic <Client ID/Client Secret>' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'username=user@iqm.com' \
--data-urlencode 'password=<your password>'
Response 200
{
"success": true,
"data":
{
"access_token": "106adb25-37b0-4cab-8381-d682fe7cc3c8",
"refresh_token": "eac4c1f6-781e-4b04-baff-9c2e415d1f64",
"scope": "read write",
"token_type": "bearer",
"expires_in": 35999,
"owId": 200001
}
}
More responses
Response 400
{
"success": false,
"data":
{
"status": "On Hold",
"reason": "The particular account is kept on hold due to missed payment dates for last 3 months.",
"supportEmail": "support@iqm.com"
},
"errorObjects":
[
{
"error": "User is not allowed to access provided customer",
"reason": "User is not associated with any active organization."
}
]
}
Response 403
{
"success": false,
"errorObjects":
[
{
"error": "User doesn't exist or user is not allowed to provided workspace."
}
]
}

Authentication​

Use the following header parameters for all requests after logging in:

Headers
Authorization
string required
Bearer followed by the access_token from the login response.
See Sign up and authenticate.
X-IAA-OW-ID
integer required
Organization Workspace ID header.

Create a Political proposal​

This tutorial covers the standard path for Reach forecasting and Budget estimation plans:

  1. Create a ready plan
  2. Get the plan's strategies
  3. Activate a strategy into a proposal
  4. Check proposal details
  5. Generate an IO and campaigns

Voter TV+ (cross-screen) plans follow a different path. See Voter TV+ proposals after completing the standard path. To target a Custom Voter audience (CVA), see Target a Custom Voter audience before Step 1.

Step 1: Create a ready plan​

POST
POST

Create a ready (or draft) plan with a budget, flight dates, and targeting. When a ready plan is created, IQM sends its details to the forecasting service, which generates the plan's strategies. Save the planId from the response for Step 2.

See Political Planner resource properties for the full list of fields and their descriptions.

note

The forecasting service decides which targeting and budget combinations can be planned, and there is no fixed list of valid combinations. A plausible request can still be rejected (response 422). If this happens, nothing is created: widen the targeting or change the budget and try again.

Request schema
name
string required
Plan name, up to 255 characters.
objectiveId
integer required
Plan objective ID.
Use 1 or 2 for this tutorial. Voter TV+ (3) follows the Voter TV+ path.
Supported values
1Reach forecasting
2Budget estimation
3Voter TV+
totalBudget
number required
Total plan budget.
details
object required
Flight dates, frequency cap, and margin.
details object properties
startDate
integer required
Unix epoch start date in milliseconds; can't be in the past.
endDate
integer required
Unix epoch end date in milliseconds; must be at least 30 minutes after startDate.
frequencyCap
integer
Frequency cap, 1 to 999.
marginValue
integer
Margin applied on top of the base CPM, as a percentage from 0 to 100 (not a currency amount). Defaults to the Customer's margin.
targeting
object required
Plan targeting.
targeting object properties
whiteListedLocation
object
Locations to target.
whiteListedLocation object properties
country
integer
Country ID to target.
device
object
Devices to target.
device object properties
deviceTypeIds
array of integers
Device type IDs to target.
creative
object
Creative types to target.
creative object properties
creativeTypeIds
array of integers
Creative type IDs to target.
isTvAutoBuying
boolean
Whether TV auto buying is enabled.
cva
object
CVA to target. Send all five fields; a cva object with only id returns a generic 500 error. See Target a Custom Voter audience.
cva object properties
id
integer required
CVA ID from Create CVA details.
equation
string required
Segment equation sent when the CVA was created.
stateIds
array of integers required
State IDs sent when the CVA was created.
segmentIds
array of integers required
Numeric segment IDs of the segments in the equation, from Find segment IDs.
reach
integer required
Audience reach, from Get the audience reach.
Response properties
planId
integer
ID of the created plan.
Request sample
{
"name": "Ohio GOTV Reach Plan",
"objectiveId": 1,
"totalBudget": 50000,
"details": {
"startDate": 1793595600000,
"endDate": 1796187600000,
"frequencyCap": 3
},
"targeting": {
"whiteListedLocation": {
"country": 30100001
},
"device": {
"deviceTypeIds": [20300001, 20300004]
},
"creative": {
"creativeTypeIds": [11, 14],
"isTvAutoBuying": false
}
}
}
Response 200
{
"success": true,
"data": {
"planId": 123
}
}
Response 422
{
"success": false,
"errorObjects": [
{
"error": "No strategies meet your criteria. Edit the plan to explore more options."
}
]
}

Step 2: Get the plan's strategies​

GET

Use the planId from Step 1 to list the strategies generated for the plan. Compare the forecasted reach, impressions, budget, and cpm of each strategy and save the id of the one you want to activate in Step 3.

A strategy's budget, frequency, creative types, and campaigns are the forecasting service's recommendation, not a copy of the plan. They can differ from what you sent in Step 1: for example, a lower budget, a lower frequency cap, or only some of the plan's creative types. The forecasting service can also return fewer than three strategies.

caution

If you create extra strategies with Generate custom strategies, the id in that endpoint's response is the forecasting service's own ID, not an IQM strategy ID, and can't be activated. Always get strategy IDs from this endpoint.

Query parameters
planId
integer required
Plan ID from Step 1.
Response properties
id
integer
Strategy ID; use this to activate the strategy.
name
string
Strategy name.
strategyTypeId
integer
Strategy type ID: 1 Maximum Reach, 3 Balanced, 4 Most Economical, 5 Custom.
proposalId
integer
ID of the proposal created from this strategy, or null if it hasn't been activated.
startDate
integer
Unix epoch start date in milliseconds.
endDate
integer
Unix epoch end date in milliseconds.
duration
integer
Flight duration in days.
campaignsCount
integer
Number of proposed campaigns in the strategy.
budget
number
Forecasted budget.
reach
integer
Forecasted reach.
impressions
integer
Forecasted impressions.
cpm
number
Forecasted cost per mille (CPM).
frequency
integer
Frequency cap.
creatives
array of integers
Creative type IDs.
devices
array of integers
Device type IDs.
channels
array of integers
Channel type IDs.
Supported values
11App
14CTV
13OTT
12Web
Response 200
{
"success": true,
"data": [
{
"id": 3001,
"name": "Maximum Reach",
"startDate": 1793595600000,
"endDate": 1796187600000,
"duration": 30,
"strategyTypeId": 1,
"campaignsCount": 4,
"proposalId": null,
"budget": 50000,
"frequency": 3,
"reach": 1250000,
"impressions": 2220000,
"cpm": 22.52,
"creatives": [11, 14],
"devices": [20300001, 20300004],
"channels": [11, 12, 13, 14],
"isTvAutoBuying": false
},
{
"id": 3002,
"name": "Most Economical",
"startDate": 1793595600000,
"endDate": 1796187600000,
"duration": 30,
"strategyTypeId": 4,
"campaignsCount": 2,
"proposalId": null,
"budget": 38500,
"frequency": 3,
"reach": 980000,
"impressions": 2100000,
"cpm": 18.33,
"creatives": [11, 14],
"devices": [20300001, 20300004],
"channels": [11, 12],
"isTvAutoBuying": false
}
]
}

Step 3: Activate a strategy into a proposal​

POST

Pass the strategy id from Step 2 as strategyId to activate it. Activation creates a proposal with its proposed campaigns, in Ready (6) status. Save the proposal id from the response for the next steps.

info

Voter TV+ plans have no strategies, so they are activated with planId instead of strategyId. See Voter TV+ proposals.

Request schema
strategyId
integer required
Strategy ID from Step 2.
Response properties
id
integer
Proposal ID.
message
string
Success message.
Request sample
{
"strategyId": 3001
}
Response 200
{
"success": true,
"data": {
"id": 7,
"message": "Ohio GOTV Reach Plan saved successfully."
}
}
More responses
Response 422
{
"success": false,
"errorObjects": [
{
"error": "Strategy with strategy id 42 does not exist",
"field": "strategyId"
}
]
}

Step 4: Check proposal details​

GET

Use the proposal id from Step 3 to review the proposal and its proposed campaigns before generating an IO. The statusId must be Ready (6) or Failed (4) to continue to Step 5.

To change the proposal's budget, dates, or campaigns first, see Edit proposal.

Path parameters
proposalId
integer required
Proposal ID from Step 3.
Response properties
id
integer
Proposal ID.
name
string
Proposal name.
planId
integer
ID of the plan the proposal was created from.
planName
string
Name of the plan the proposal was created from.
objectiveId
integer
Plan objective ID.
Supported values
1Reach forecasting
2Budget estimation
3Voter TV+
statusId
integer
Proposal status ID.
Supported values
1Converted
2Draft
3Expired
4Failed
5Processing
6Ready
7IO Created
ioId
integer
IO ID generated from the proposal, once it's converted.
owId
integer
Organization Workspace ID.
startTime
integer
Unix epoch start time in milliseconds.
endTime
integer
Unix epoch end time in milliseconds.
timeZoneId
integer
Timezone ID.
timeZoneName
string
Timezone name.
frequencyCap
integer
Frequency cap.
minReach
integer
Minimum forecasted reach.
reach
integer
Forecasted reach.
maxReach
integer
Maximum forecasted reach.
minImpressions
integer
Minimum forecasted impressions.
impressions
integer
Forecasted impressions.
maxImpressions
integer
Maximum forecasted impressions.
minBudget
number
Minimum budget.
budget
number
Proposal budget.
maxBudget
number
Maximum budget.
minCpm
number
Minimum cost per mille (CPM); can be null at the proposal level.
cpm
number
CPM; can be null at the proposal level. Each proposed campaign has its own CPM.
maxCpm
number
Maximum CPM; can be null at the proposal level.
campaignGeneratable
boolean
Whether an IO and campaigns can be generated from the proposal. Only meaningful while the proposal is Ready or Failed; it isn't reset after the proposal is converted.
proposedCampaigns
array of objects
Campaigns that are created when the IO is generated.
proposedCampaigns object properties
id
integer
Proposed campaign ID.
name
string
Campaign name.
type
string
Voter TV+ proposals only: CTV or Linear TV. Only CTV campaigns are created when the IO is generated. null for other proposals.
strategy
string
Targeting dimensions the campaign was split by.
creativeTypeId
integer
Creative type ID.
startTime
integer
Unix epoch start time in milliseconds.
endTime
integer
Unix epoch end time in milliseconds.
duration
integer
Flight duration in days.
modelType
string
Pricing model; always CPM.
frequencyCap
integer
Frequency cap.
minTargetedReach
integer
Minimum targeted reach.
targetedReach
integer
Targeted reach.
maxTargetedReach
integer
Maximum targeted reach.
minTargetedImpressions
integer
Minimum targeted impressions.
targetedImpressions
integer
Targeted impressions.
maxTargetedImpressions
integer
Maximum targeted impressions.
minCpm
number
Minimum CPM.
cpm
number
CPM.
maxCpm
number
Maximum CPM.
minBudget
number
Minimum budget.
budget
number
Campaign budget.
maxBudget
number
Maximum budget.
Response 200
{
"success": true,
"data": {
"id": 7,
"name": "Ohio GOTV Reach Plan",
"planId": 123,
"planName": "Ohio GOTV Reach Plan",
"objectiveId": 1,
"startTime": 1793595600000,
"endTime": 1796187600000,
"owId": 202500,
"statusId": 6,
"ioId": null,
"reach": 1250000,
"impressions": 2220000,
"budget": 50000,
"frequencyCap": 3,
"timeZoneId": 29,
"timeZoneName": "US/Eastern",
"campaignGeneratable": true,
"proposedCampaigns": [
{
"id": 101,
"name": "Ohio GOTV Reach Plan - CTV",
"startTime": 1793595600000,
"endTime": 1796187600000,
"creativeTypeId": 14,
"duration": 30,
"modelType": "CPM",
"targetedReach": 500000,
"frequencyCap": 3,
"targetedImpressions": 1500000,
"cpm": 25.5,
"budget": 38250,
"strategy": "Device + Channel"
}
]
}
}

Step 5: Generate an IO and campaigns​

POST

Convert the proposal into an IO and campaigns. The proposal must be in Ready (6) or Failed (4) status.

The request returns immediately and the proposal moves to Processing (5) while the IO and campaigns are created in the background. When generation finishes, the proposal moves to Converted (1) and its ioId is set. If generation fails, the proposal moves to Failed (4) and you can retry this step. Use Step 4 to check the result.

The converted proposal doesn't link its proposed campaigns to the campaigns that were created. To find the new campaigns, call Get basic details of campaigns (POST /api/v3/cmp/basic/list) with the proposal's ioId in ioIdsList, for example {"ioIdsList": [29046]}. The campaigns are created as drafts.

Manage the generated IO and campaigns with the Campaign API.

danger

Generating an IO is a one-way step. A converted proposal can't be returned to Ready, and calls that require a Ready, Failed, or Expired proposal, such as editing, downloading, or restoring, no longer work for it.

Path parameters
proposalId
integer required
Proposal ID from Step 3.
Request schema
politicalAdvertiserId
integer required
Political Advertiser profile ID.
startTime
integer required
Unix epoch start time for the campaigns, in milliseconds; can't be in the past. Always required.
endTime
integer
Unix epoch end time in milliseconds. If omitted, the proposal's current duration is kept from the new startTime. If provided, it must keep the same duration in days.
Request sample
{
"politicalAdvertiserId": 789,
"startTime": 1793595600000
}
Response 200
{
"success": true,
"data": {
"message": "Campaign generation is in progress"
}
}
More responses
Response 422
{
"success": false,
"errorObjects": [
{
"error": "proposal start and end date changes must preserve the existing duration"
}
]
}

Voter TV+ proposals​

Voter TV+ (objective 3) plans forecast reach across linear TV and connected TV (CTV). They follow a different path from the standard steps above:

  1. Get a reach saturation graph
  2. Create a ready plan with the graph
  3. Activate the plan into a proposal
  4. Split the proposal's CTV campaigns (optional)
  5. Generate an IO and campaigns, as in Step 5 of the standard path

When the IO is generated, only the CTV proposed campaigns become campaigns; linear TV campaigns aren't created.

Eligibility requirements​

Voter TV+ plans:

Step 1: Get a reach saturation graph​

POST

Send the plan's budget, dates, and targeting with objectiveId 3 to get a reach forecast for linear TV and CTV. Save the graphId and overview from the response for the next step.

Check audienceOverlap.totalReach in the response before continuing. If it's 0, the targeting doesn't match any voters: a plan created from this graph produces a proposal with 0 budgets, and splitting it fails. Narrow the location targeting, for example to states or DMAs, and request a new graph. The sample targets Ohio (30200036) and Pennsylvania (30200039).

To adjust the forecast, call this endpoint again with the same graphId and new overview values.

See Get reach saturation graph for all request and response fields.

tip

The first request for a new graph calls an external data service and can take more than 60 seconds. Set your client timeout to at least 60 seconds so the request isn't mistaken for a failure. Later requests with the same graphId are calculated without the external call.

Request schema
name
string required
Plan name.
objectiveId
integer required
Plan objective ID: 3 (Voter TV+).
totalBudget
number required
Total plan budget; must be at least 100.
details
object required
Flight dates and frequency cap.
details object properties
startDate
integer required
Unix epoch start date in milliseconds; can't be in the past.
endDate
integer required
Unix epoch end date in milliseconds. The flight must be 7 to 31 days.
frequencyCap
integer
Frequency cap, 1 to 999 (default: 3).
targeting
object
Locations, demographics, and audiences to forecast reach for.
targeting object properties
whiteListedLocation
object
Locations to target.
whiteListedLocation object properties
country
integer
Country ID to target.
states
array of integers
State IDs to target.
ageGroupSegments
array of integers
Age group demographic segment IDs.
genderSegments
array of integers
Gender demographic segment IDs.
incomeRangeSegments
array of integers
Income range demographic segment IDs.
ethnicitySegments
array of integers
Ethnicity demographic segment IDs.
partyAffiliationSegments
array of integers
Party affiliation demographic segment IDs.
whitelistedAudiences
array of integers
Audience IDs to target. Power Segment audiences aren't supported for Voter TV+.
graphId
string
ID of a graph you already created, to recalculate it with new overview values. Omit to create a new graph.
overview
object
Linear TV and CTV assumptions for the forecast. Defaults are used if omitted.
overview object properties
linearTvCpm
number
Linear TV cost per mille (CPM); 0 or more.
ctvCpm
number
CTV CPM; 0 or more.
grp
number
Linear TV gross rating points (GRP); 0 or more.
linearTvPercentage
integer
Percentage of the budget allocated to linear TV, 0 to 100. The rest goes to CTV.
ctvFrequency
integer
Average CTV frequency, 1 to 99.
Request sample
{
"name": "Voter TV+ Plan",
"objectiveId": 3,
"totalBudget": 100000,
"details": {
"startDate": 1793595600000,
"endDate": 1794805200000,
"frequencyCap": 3
},
"targeting": {
"whiteListedLocation": {
"country": 30100001,
"states": [30200036, 30200039]
},
"ageGroupSegments": [6073147, 6073145],
"genderSegments": [6189394, 6189395]
}
}
Response 200
{
"success": true,
"data": {
"graphId": "0f6c7400-a8f5-4a1b-ab65-af15372cc0ff",
"overview": {
"linearTvCpm": 20,
"ctvCpm": 7,
"grp": 180,
"linearTvPercentage": 50,
"ctvFrequency": 3
},
"audienceOverlap": {
"population": 16100000,
"totalReach": 1470000,
"linearTvReach": 1020000,
"ctvReach": 778000,
"overlapReach": 328000,
"ctvIncrementalReach": 450000
},
"maximumAudienceOverlap": {
"population": 16100000,
"totalReach": 2150000,
"linearTvReach": 1480000,
"ctvReach": 1150000,
"overlapReach": 480000,
"ctvIncrementalReach": 670000
},
"reachSaturation": [
{
"day": 1,
"ctvReach": 12,
"linearTvReach": 14,
"overallReach": 20
}
]
}
}

Step 2: Create a Voter TV+ plan​

POST

Create a ready plan with objectiveId 3, and copy the graphId and overview from your Step 1 response. Use the same budget, dates, and targeting you sent in Step 1 so the plan matches the graph. Save the planId from the response.

info

Voter TV+ plans always target video creatives on CTV devices. Any targeting.creative or targeting.device values in the request are replaced.

Voter TV+ plans don't generate strategies, so Get the plan's strategies returns an empty list. Activate the plan directly in the next step.

Request schema
name
string required
Plan name, up to 255 characters.
objectiveId
integer required
Plan objective ID: 3 (Voter TV+).
totalBudget
number required
Total plan budget; must be more than 100.
graphId
string required
graphId from Step 1.
overview
object
overview from Step 1. Defaults are used if omitted.
overview object properties
linearTvCpm
number
Linear TV cost per mille (CPM); 0 or more.
ctvCpm
number
CTV CPM; 0 or more.
grp
number
Linear TV gross rating points (GRP); 0 or more.
linearTvPercentage
integer
Percentage of the budget allocated to linear TV, 0 to 100. The rest goes to CTV.
ctvFrequency
integer
Average CTV frequency, 1 to 99.
details
object required
Flight dates, frequency cap, and margin.
details object properties
startDate
integer required
Unix epoch start date in milliseconds; can't be in the past.
endDate
integer required
Unix epoch end date in milliseconds. The flight must be 7 to 31 days.
frequencyCap
integer
Frequency cap, 1 to 999 (default: 3).
marginValue
integer
Margin applied on top of the base CPM, as a percentage from 0 to 100 (not a currency amount). Defaults to the Customer's margin.
targeting
object required
Locations, demographics, and audiences to target.
targeting object properties
whiteListedLocation
object
Locations to target.
whiteListedLocation object properties
country
integer
Country ID to target.
states
array of integers
State IDs to target.
ageGroupSegments
array of integers
Age group demographic segment IDs.
genderSegments
array of integers
Gender demographic segment IDs.
incomeRangeSegments
array of integers
Income range demographic segment IDs.
ethnicitySegments
array of integers
Ethnicity demographic segment IDs.
partyAffiliationSegments
array of integers
Party affiliation demographic segment IDs.
whitelistedAudiences
array of integers
Audience IDs to target. Power Segment audiences aren't supported for Voter TV+.
Request sample
{
"name": "Voter TV+ Plan",
"objectiveId": 3,
"totalBudget": 100000,
"graphId": "0f6c7400-a8f5-4a1b-ab65-af15372cc0ff",
"overview": {
"linearTvCpm": 20,
"ctvCpm": 7,
"grp": 180,
"linearTvPercentage": 50,
"ctvFrequency": 3
},
"details": {
"startDate": 1793595600000,
"endDate": 1794805200000,
"frequencyCap": 3
},
"targeting": {
"whiteListedLocation": {
"country": 30100001,
"states": [30200036, 30200039]
},
"ageGroupSegments": [6073147, 6073145],
"genderSegments": [6189394, 6189395]
}
}
Response 200
{
"success": true,
"data": {
"planId": 124
}
}
More responses
Response 422
{
"success": false,
"errorObjects": [
{
"error": "Duration in days must be between 7 and 31"
}
]
}

Step 3: Activate a Voter TV+ plan​

POST

Pass the planId from the previous step to create a proposal from the plan's graph. The budget is split between linear TV and CTV campaigns using the linearTvPercentage from the overview. The proposal is created in Ready (6) status.

Request schema
planId
integer required
Voter TV+ plan ID. Don't send strategyId in the same request.
Request sample
{
"planId": 124
}
Response 200
{
"success": true,
"data": {
"id": 8,
"message": "Voter TV+ Plan_Mix_01 saved successfully."
}
}

Step 4: Split a Voter TV+ proposal​

POST

Split a Voter TV+ proposal's CTV campaigns into one campaign per value of a targeting dimension, such as one campaign per state. The existing CTV campaigns are replaced. Send an empty locations list to merge them back into a single CTV campaign. This step is optional.

A split only has an effect for dimensions that the plan targets with more than one value. The sample splits by state (8), because the sample plan targets two states; splitting it by ZIP code or device gives a single CTV campaign. Split campaigns are named with a numbered suffix, such as _Video + Loc_1 and _Loc_2, not with the state name.

The response shows the proposal as it was before the split. To see the new campaigns, get the proposal again with Check proposal details.

This endpoint only works on Voter TV+ proposals. On a proposal with 0 budgets, such as one created from a graph with no reach, it returns a generic 500 error.

tip

Splitting can take more than 60 seconds for large proposals. Set your client timeout to at least 60 seconds.

Path parameters
proposalId
integer required
Voter TV+ proposal ID.
Request schema
locations
array of integers
Targeting type IDs to split by. Despite the field name, these are targeting dimension IDs, not location IDs. For example, 8 (state), 12 (DMA), 15 (ZIP code), 29 (device).
Request sample
{
"locations": [8]
}
Response 200 (abbreviated)
{
"success": true,
"data": {
"id": 8,
"name": "Voter TV+ Plan_Mix_01",
"startTime": 1793595600000,
"endTime": 1794805200000,
"owId": 202500,
"statusId": 6,
"reach": 778000,
"impressions": 7140000,
"budget": 100000,
"frequencyCap": 3,
"campaignGeneratable": true,
"proposedCampaigns": [
{
"id": 501,
"name": "Voter TV+ Plan_Mix_01_VTV + Video",
"type": "Linear TV",
"budget": 50000
},
{
"id": 502,
"name": "Voter TV+ Plan_Mix_01_Video",
"type": "CTV",
"budget": 50000
}
]
}
}

Target a Custom Voter audience​

A Custom Voter audience (CVA) combines voter segments into a single audience for a plan. To target a CVA:

  1. Find the segment IDs
  2. Get the audience reach
  3. Create CVA details
  4. Add the CVA to a plan

When you add the CVA to a plan, send the full cva object: id, equation, stateIds, segmentIds, and reach. A plan request with only cva.id returns a generic 500 error, even though the other fields are stored with the CVA ID.

Step 1: Find segment IDs​

POST

Look up the segments you want to combine. Each leaf segment has a taxonomyId, such as IQMCVACA069112, which is the token used in the CVA equation, and a numeric segmentId, which goes in the plan's cva.segmentIds. Save both.

To browse the segment catalog instead of looking up known taxonomy IDs, see Get Custom Voter audience segment details.

Request schema
taxonomyIds
array of strings
Taxonomy IDs of the segments to look up.
validateTaxonomyIds
boolean
Set to true to return an error if any taxonomy ID doesn't exist (default: false).
Response properties
cvaSegmentData
array of objects
Matching segments.
cvaSegmentData object properties
segmentId
integer
Segment ID; use it in cva.segmentIds.
taxonomyId
string
Taxonomy ID; use it in the CVA equation.
taxonomyValue
string
Segment name.
isLeaf
boolean
Whether the segment is a leaf segment. Only leaf segments can be used in an equation.
Request sample
{
"taxonomyIds": ["IQMCVACA069112", "IQMCVACA080323", "IQMCVACA101616"],
"validateTaxonomyIds": true
}
Response 200 (abbreviated)
{
"success": true,
"data": {
"totalRecords": 3,
"filteredRecords": 3,
"cvaSegmentData": [
{ "segmentId": 4211, "taxonomyId": "IQMCVACA069112", "isLeaf": true },
{ "segmentId": 4215, "taxonomyId": "IQMCVACA080323", "isLeaf": true },
{ "segmentId": 4230, "taxonomyId": "IQMCVACA101616", "isLeaf": true }
]
}
}

Step 2: Get the audience reach​

POST

Get the number of voters that match the equation. Use segment_num_voters from the response as the CVA's reach in the next step; IQM stores the reach you send and passes it to the forecasting service without recalculating it.

This endpoint uses snake_case state_ids, unlike the camelCase stateIds in the next step. If state_ids is empty, the response counts no voters, so send the states your segments belong to.

Request schema
equation
string required
CVA equation; see Step 3 for the syntax.
state_ids
array of integers required
State IDs of the segments.
Response properties
segment_num_voters
integer
Number of voters that match the equation; use it as the CVA's reach.
total_num_voters
integer
Total number of voters in the selected states.
map_metrics
object
Voter counts by party, keyed by location ID. Can include keys that aren't state IDs.
Request sample
{
"equation": "((IQMCVACA069112|(IQMCVACA080323))&(IQMCVACA101616))",
"state_ids": [30200005]
}
Response 200
{
"success": true,
"data": {
"segment_num_voters": 1629051,
"total_num_voters": 23165810,
"map_metrics": {
"30200005": {
"Republican": 412870,
"Democrat": 761304,
"Other": 454877
}
}
}
}

Step 3: Create CVA details​

POST

Define the CVA with an equation of the taxonomy IDs from Step 1, and the reach from Step 2.

Equation syntax:

  • Wrap each segment ID in parentheses
  • Separate segments with | to combine them (OR)
  • Separate groups with & to intersect them (AND)

For example, ((IQMCVACA069112|(IQMCVACA080323))&(IQMCVACA101616)) targets voters in either of the first two segments who are also in the third.

An equation with an unknown taxonomy ID or unbalanced parentheses returns a 500 error with the message Invalid CVA details provided. This is a validation error, not an outage: fix the equation and try again.

Request schema
equation
string required
Segment equation.
stateIds
array of integers
State IDs to limit the audience to.
reach
integer
Audience reach: segment_num_voters from Step 2. This value is stored and passed to the forecasting service as provided; it isn't calculated.
Response properties
id
integer
CVA ID; pass it as cva.id when creating a plan.
Request sample
{
"equation": "((IQMCVACA069112|(IQMCVACA080323))&(IQMCVACA101616))",
"stateIds": [30200005],
"reach": 1629051
}
Response 200
{
"success": true,
"data": {
"id": 12345
}
}

Add the CVA to a plan​

Pass the full CVA in the cva field when you create a ready plan: the id from Step 3, the equation and stateIds from Step 3, the segmentIds from Step 1, and the reach from Step 2. To keep the forecast consistent, target the CVA's states in the plan's whiteListedLocation as well.

cva object
"cva": {
"id": 12345,
"equation": "((IQMCVACA069112|(IQMCVACA080323))&(IQMCVACA101616))",
"stateIds": [30200005],
"segmentIds": [4211, 4215, 4230],
"reach": 1629051
}

Restore an expired proposal​

PATCH

A proposal that isn't converted becomes Expired (3) after its end date passes. To use an expired proposal again, restore it with new flight dates. Restored proposals return to Ready (6) status and can then be converted as in Step 5.

The new flight can be a different length from the original; unlike generating an IO, restoring doesn't require the duration to stay the same. Restoring only works on expired proposals. It isn't an undo for Delete proposals.

Request schema
proposalIds
array of integers required
IDs of expired proposals to restore.
startTime
integer required
New Unix epoch start time in milliseconds; can't be in the past.
endTime
integer required
New Unix epoch end time in milliseconds; must be at least 30 minutes after startTime.
Request sample
{
"proposalIds": [7],
"startTime": 1793595600000,
"endTime": 1796187600000
}
Response 200
{
"success": true,
"data": {
"message": "Proposals restored successfully"
}
}
More responses
Response 422
{
"success": false,
"errorObjects": [
{
"error": "One or more proposals are not in expired status",
"field": "proposalIds"
}
]
}
Have a question?
Was this page helpful?