Third-party data usage and spend
The thirdPartyDataUsageReporting GraphQL query provides on-demand access to third-party data usage and spend across a partner's advertisers. This eliminates the wait for emailed reports (such as for billing) and gives you greater control through improved visibility and reduced complexity.
Intended use
You can use the thirdPartyDataUsageReporting query to do the following:
- Pull impression counts and gross data costs for a specific data provider.
- Investigate spend across partners, advertisers, campaigns, and ad groups.
- Answer billing-related questions using data from the current month.
- Look up segment-level usage for large data taxonomies (larger than one million segments).
NOTE: Your existing API login is used for The Trade Desk platform authentication. No additional roles or permissions are required, and the
thirdPartyDataUsageReportingGraphQL query accesses partner data using the existing tokens.
Behavior and limitations
Here's what you need to know about the thirdPartyDataUsageReporting query:
- Scoped to a third-party data provider using its ID in the
thirdPartyDataProviderfield. - Generates the same data used in the batch-generated reports emailed to data partners.
- Returns directional, intra-month estimates and does not include month-end actuals that reflect the marketplace participation fee (MPF).
- Supports a rolling 60-day lookback period.
- Reflects data rolled up daily at the end of day (UTC), so the most recent data available is from the previous day. Same-day reporting is not supported.
The following sections cover key concepts and provide a query example for checking usage reporting data from a third-party data provider as well as an advanced query example for looking up more than one million data segments at once. There is also a reference for the fields returned by this query.
Filter by date
The thirdPartyDataUsageReporting query supports date filtering through the where block. The date filter is optional. If not used, the API automatically applies a default 31-day window ending at the current date. When using date filters, be aware of the following limitations:
| Limitation | Description | Notes |
|---|---|---|
| Lookback window | Your start date must fall within the previous 60 days. | Queries with a start date older than 60 days will return a validation error. |
| Maximum date range | A single query can span a maximum of 31 days. | If you need data covering a longer period, issue multiple queries in consecutive windows and combine the results. |
| Granularity | Daily is the finest level of granularity available. | Hourly breakdowns are not supported. |
Here are additional tips for optimizing date filtering:
- Be deliberate. Always provide both the start date (
gte) and end date (lt) values when using date filters. If no date filter is provided, or if only agtevalue is provided withoutltvalue, the API automatically constructs a 31-day window and no error is thrown. This may return a narrower or different window than intended. For predictable results, always specify both bounds explicitly. - Use the
ltvalue for your end date, notlte. This avoids off-by-one errors and makes multi-window queries easier to reason about.
Dates are passed in the where block using the gte (greater than or equal to) and lt (less than) operators. To ensure your query covers the intended range, provide both bounds explicitly, as shown in the following snippet.
where: {
date: {
gte: "2026-03-01"
lt: "2026-04-01"
}
}
NOTE: All dates are interpreted as UTC.
Define date ranges
To capture complete data with non-overlapping windows, use only the gte and lt fields, not gt or lte. Using lt for the end date captures the entire last day and keeps the query ranges clean. For example, to retrieve all of February 2026, use the following snippet:
where: {
date: {
gte: "2026-02-01" # correct
lt: "2026-03-01" # correct
}
}
To cover March and April 2026 as two separate queries, use the following snippet:
# Query 1 — March
where: { date: { gte: "2026-03-01", lt: "2026-04-01" } }
# Query 2 — April
where: { date: { gte: "2026-04-01", lt: "2026-05-01" } }
Using lte: "2026-03-31" instead would match only up to the start of that day in UTC, meaning data for the remainder of March 31 could be missed. Using lt: "2026-04-01" is the explicit and reliable way to include all of March 31.
Query up to 60 days
To query the full 60-day window, split your request into two calls, one for days 60–30 and one for days 30–0, since each query is capped at 31 days. Paginate each call independently until the hasNextPage value is false, then sum the dataCostInUSD value and impressions across all nodes from both result sets.
NOTE: Update the following query examples each time you run the query, as hardcoded dates will eventually fall outside the 60-day lookback period. If you're automating this workflow, calculate date windows dynamically each run.
Here's an example of the ThirdPartyDataProviderWindowOne query for days 60-30.
query ThirdPartyDataProviderWindowOneExample {
thirdPartyDataProvider(id: "3PD_PROVIDER_ID_PLACEHOLDER") {
reporting {
thirdPartyDataUsageReporting(
where: {
date: {
gte: "2026-02-06"
lt: "2026-03-08"
}
}
first: 10000
after: null
) {
totalCount
nodes {
metrics {
dataCostInUSD
impressions
}
dimensions {
thirdPartyDataBrand {
name
id
}
}
}
pageInfo {
endCursor
hasNextPage
}
}
}
}
}
And here's an example of the ThirdPartyDataProviderWindowTwo query for days 30 to today.
query ThirdPartyDataProviderWindowTwoExample {
thirdPartyDataProvider(id: "3PD_PROVIDER_ID_PLACEHOLDER") {
reporting {
thirdPartyDataUsageReporting(
where: {
date: {
gte: "2026-03-08"
lt: "2026-04-07"
}
}
first: 10000
after: null
) {
totalCount
nodes {
metrics {
dataCostInUSD
impressions
}
dimensions {
thirdPartyDataBrand {
name
id
}
}
}
pageInfo {
endCursor
hasNextPage
}
}
}
}
}
Filter by brand ID
You can filter results to a specific third-party data brand using the brandId filter inside the where block. It accepts an eq (equals) operator with a string value matching the brand ID.
The brandId filter can be used on its own or combined with a date filter in the same where block, as shown in the following snippet.
NOTE: Passing an empty string or a value that parses incorrectly will return an error. See [Filter Input Errors]. Passing a valid but non-existent brand ID will return empty results with no error.
thirdPartyDataUsageReporting(
where: {
brandId: { eq: "YOUR_BRAND_ID" }
date: { gte: "2026-03-01", lt: "2026-04-01" }
}
first: 10000
after: null
)
Paginate results
When using pagination (cursor-based), you can retrieve up to 10,000 pages in a single call. For example, set the first field to 10000 to return 10,000 pages.
IMPORTANT: Never assume a single call returns all data. Be sure to verify the
hasNextPagefield. If it is set tofalse, then all data has been returned.
To use pagination, do the following:
- In the first call, set the
afterfield tonull. - In subsequent calls, pass the
endCursorvalue from thepageInfofield as theafterargument. - Stop pagination when the
hasNextPagefield is set tofalse.
Look up data usage
Here's a thirdPartyDataUsageReporting query example for retrieving usage reporting data for a specific third-party data provider by the specified date range, limited to one node per page. It returns metrics like impressions and cost, as well as dimensions such as time, partner, advertiser, campaign, ad group, and data provider details.
query GetThirdPartyDataUsageExample {
thirdPartyDataProvider(id: "3PD_PROVIDER_ID_PLACEHOLDER") {
reporting {
thirdPartyDataUsageReporting(
after: "END_CURSOR_TOKEN" # First call can be null. After that, pass in the value returned in the previous iteration.
where: {
date: {
gte: "ISO_8601_START_DATE_PLACEHOLDER"
lte: "ISO_8601_END_DATE_PLACEHOLDER"
}
}
first: 10000
) {
pageInfo {
endCursor
hasNextPage
}
totalCount
nodes {
dimensions {
partnerBasicInfo {
name
id
}
advertiserBasicInfo {
name
id
}
campaignBasicInfo {
name
id
}
adGroupBasicInfo {
name
id
}
thirdPartyData {
targetingData {
id
}
fullPath
providerElementId
}
thirdPartyDataBrand {
name
id
}
}
metrics {
impressions
dataCostInUSD
}
}
}
}
}
}
Look up large taxonomy (over one million segments)
If the provider has more than one million data segments, use the following workflow:
- Retrieve segment IDs only.
- Retrieve dimensions for the segment IDs.
Retrieve segment IDs only
Use the thirdPartyDataUsagePGIdsOnly query with the thirdPartyData.id field to retrieve segment IDs first, as shown in the following example:
query thirdPartyDataUsagePGIdsOnlyExample {
thirdPartyDataProvider(id: "3PD_PROVIDER_ID_PLACEHOLDER") {
reporting {
thirdPartyDataUsageReporting(
first: 10000
where: {
date: {
gte: "2026-02-01"
lt: "2026-03-01"
}
}
after: "PREV_END_CURSOR_TOKEN" # null on first call
) {
pageInfo {
endCursor
hasNextPage
}
totalCount
nodes {
dimensions {
thirdPartyData {
id
}
}
}
}
}
}
}
Retrieve dimensions for segment IDs
To retrieve comprehensive dimensions and metrics, run the thirdPartyDataUsageReporting query and include the segment IDs obtained from the previous step in the thirdPartyDataIds filter. For best performance, group the IDs into batches of up to 1000 per request:
query thirdPartyDataUsageExample {
thirdPartyDataProvider(id: "3PD_PROVIDER_ID_PLACEHOLDER") {
reporting {
thirdPartyDataUsageReporting(
after: "END_CURSOR_TOKEN" # First call can be null. After that, pass in the value returned in the previous iteration.
where: {
date: {
gte: "ISO_8601_START_DATE_PLACEHOLDER"
lte: "ISO_8601_END_DATE_PLACEHOLDER"
},
thirdPartyDataIds: {
in: [
"SEGMENT_ID_PLACEHOLDER_1",
"SEGMENT_ID_PLACEHOLDER_2",
"SEGMENT_ID_PLACEHOLDER_3",
"SEGMENT_ID_PLACEHOLDER_4" # Add more IDs here as needed. For best results, fetch up to 1000 IDs at a time.
]
}
}
first: 10000
) {
pageInfo {
endCursor
hasNextPage
}
totalCount
nodes {
dimensions {
partnerBasicInfo {
name
id
}
advertiserBasicInfo {
name
id
}
campaignBasicInfo {
name
id
}
adGroupBasicInfo {
name
id
}
thirdPartyData {
targetingData {
id
}
fullPath
providerElementId
}
thirdPartyDataBrand {
name
id
}
}
metrics {
impressions
dataCostInUSD
}
}
}
}
}
}
Field reference
The following table lists different fields that the thirdPartyDataUsageReporting query returns.
| Field Name | Description |
|---|---|
partnerBasicInfo.name |
The name of the partner associated with the advertiser included in the data usage report. |
partnerBasicInfo.id |
The ID of the partner. |
advertiserBasicInfo.name |
The name of the advertiser included in the data usage report. |
advertiserBasicInfo.id |
The ID of the advertiser. |
campaignBasicInfo.name |
The name of the campaign included in the data usage report. |
campaignBasicInfo.id |
The unique platform ID assigned to the campaign. |
adGroupBasicInfo.name |
The name of the ad group within the campaign included in the data usage report. |
adGroupBasicInfo.id |
The unique platform ID assigned to the ad group. |
thirdPartyData.id |
The unique ID of the third-party data provider. |
thirdPartyData.providerElementId |
The unique ID you (the third-party provider) assigned to the segment when designing your taxonomy. Also known as the segment ID, this ID is used to identify a data segment that you uploaded. |
thirdPartyData.targetingData.id |
The advertiser targeting data ID from the targeting data group. |
thirdPartyData.fullPath |
The full hierarchical path representing the data segment within the third-party data taxonomy. |
thirdPartyDataBrand.name |
The name of the brand associated with the third-party data provider. |
thirdPartyDataBrand.id |
The third-party data brand ID associated with the third party data ID in the targeting data group. |
impressions |
The total number of ad impressions served. |
dataCostInUSD |
The total cost of the data usage in U.S. dollars. |
Error messages and codes
The following sections cover common errors, validation rules, and silent failure conditions you may encounter using the thirdPartyDataUsageReporting query.
Authorization
The API checks the Permission.General_Edit field on the ThirdPartyDataProvider entity. Dimensions and data values are not subject to separate permission checks. If you receive a "not found" error, confirm the following before escalating:
- The provider ID is correct.
- The token has access to the provider.
The following table describes "not found" error messages and their root cause.
| Message | Error |
|---|---|
"The entity was not found." |
Provider ID not authorized |
"At least one of the entities were not found." |
Bulk authorization failure |
Date range
Keep the following limitations in mind when setting the date range for your query.
- The start date must fall within the last 60 days.
- A single query cannot span more than 31 days. This applies to both rolled-up and day-by-day queries. If you provide only one bound (for example, a
gtevalue without theltvalue), the API auto-constructs the missing bound using a 31-day default and returns no error, but the resulting window may be narrower than intended. Always provide both bounds explicitly. - The
Dayvalue is the finest supported granularity. Hourly breakdowns are not supported and always return a validation error.
The following table describes date-range error messages and their root cause.
| Message | Error |
|---|---|
"The requested time range exceeds the allowed limit. Start date must be on or after {date} UTC (up to 60 days prior to today)." |
Start date beyond 60-day lookback (no time dimension) |
"The requested date range is too long. Limit daily aggregated queries to 31 days." |
Date range exceeds 31 days (no time dimension) |
"The requested time range for daily reports exceeds the allowed limit..." |
Start date beyond lookback or range exceeds 31 days (with Day dimension) |
"The requested time range for hourly reports exceeds the allowed limit..." |
Any hourly or datetime dimension query |
Filter input
The following table describes filter-input error messages and their root cause.
NOTE: Date filters default to UTC if no timezone is specified.
| Message | Error |
|---|---|
"Unable to parse value {value} as a datetime." |
Unparseable date in date filter |
"Attempted to pass in a null or empty string for an eq operation." |
Empty string for brandId (eq) |
"Attempted to pass in a null or empty list for an in operation." |
Empty list for thirdPartyDataIds (in) |
"Attempted to pass in an invalid value for an eq operation." |
Invalid type for thirdPartyDataIds (eq) |
"Attempted to pass in an invalid value in an in operation." |
Invalid type in thirdPartyDataIds list (in) |
"Attempted to pass in an empty list for an in operation." |
Empty list after parsing thirdPartyDataIds (in) |
Silent failures
Silent failures are the most dangerous errors. The query succeeds but returns incomplete or empty data with no indication that something is wrong.The following table describes each silent failure scenario.
| Error | Description |
|---|---|
Valid format but unrecognized thirdPartyDataIds or brandId values. |
Returns empty results with no error. Always verify IDs before querying. |
Day dimension used without the required feature flag. |
The dimension is silently omitted from results rather than returning an error. If a provider reports missing time-breakdown data, confirm that the ReportingMetricsAccessThirdPartyDataReportingDataUsageTimeDimensionFeatureFlag flag is enabled for their account. |