Skip to main content
Sales, legal, and procurement teams often store contracts in Box with structured metadata, for example, contract type, value, effective dates, and status. Listing every file to compute totals in your application is slow, expensive, and hard to keep current as content grows. The Box Query Insights API solves this by returning aggregated metrics directly from Box. You filter items with the same predicate syntax as Box Query API, optionally group results, and request metrics such as count, sum, avg, min, and max in a single API call. This tutorial builds a contract analytics dashboard with total, average, minimum, and maximum contract value for sales contracts in a date range.

Clone the working sample

Prefer to start from running code? The complete app built in this tutorial is on GitHub. Clone it, add your Box credentials, and run.

Use cases

The Box Query Insights API fits any workflow where you need summary numbers instead of individual file lists.
The Box Query Insights API applies filters first, then grouping, then metric calculation. It does not return individual items. When you need file-level results, use Box Query API or metadata queries and compute aggregates in your app, or call both APIs for drill-down views.

Before you start

You need:
  • A free Box developer account.
  • A Box application configured with Client Credentials Grant (CCG) authentication.
  • The Read and write all files and folders stored in Box scope enabled on your app.
  • Client Credentials Grant authorized for your enterprise in the Developer Console and Admin Console.
  • Python 3.10 or later.
  • Admin permissions to create a metadata template, or an existing template you can use instead. See Create a metadata template.

Build with an agent

The agent expects a metadata template, a contracts folder, and tagged sample files to already exist in Box. If you don’t have them yet, complete the first three steps of Build by hand, through Create a metadata template and sample data, and then return here. Get these values from the sample data generated by the script setup_test_data.py or from the metadata template and folder you created:
  • Template key - from the template you created (for example, sales)
  • Folder ID - from the folder you created (for example, 1234567890)
  • Enterprise ID - from the Developer Console (for example, 1234567890)
Choose your stack and copy the prompt. It already lists these as prerequisites, so the agent reads them from environment variables instead of trying to create them. Replace the <TEMPLATE_KEY>, <FOLDER_ID>, and <ENTERPRISE_ID> placeholders if you want the agent to pre-fill .env; otherwise leave them and fill .env yourself after scaffolding.
Python + Script

Review generated code before using it in production. Never paste Box credentials into your coding agent.

When the agent finishes, copy .env.example to .env and fill in your client ID, client secret, enterprise ID, metadata template key, and folder ID. Use a template whose contractType is an enum. Grouping on a free-text string field returns 400 Invalid query request. A CCG app stores the contracts folder in the service account root, so the folder does not display in your personal All Files. Then skip ahead to Run and verify. Prefer to write the code yourself? See Build by hand below.

Build by hand

1

Set up the development environment

  1. Create a project directory and virtual environment:
  1. Install the required packages:
  1. Create a .env file with your application credentials. Fill in the first three Client Credentials Grant values now – you add the folder and template values in the next step:
Add .env to .gitignore. Never commit credentials to version control.
2

Authenticate the Box client

Create box_client.py. Every other module imports get_box_client from here, so the credentials live in one place:
BoxCCGAuth fetches and refreshes the access token for you, so you never manage the token endpoint directly.
BOX_ENTERPRISE_ID must be the numeric enterprise ID from Developer Console > Configuration > App Details. Pasting a client ID or client secret here fails with invalid_grant: Grant credentials are invalid.
The remaining steps call client.query.create_query_insight_v2026_r0, which sends POST https://api.box.com/2.0/query_insights with the box-version: 2026.0 header. Because the method is generated from the API spec, you pass typed request objects and read a typed QueryInsightsV2026R0 result instead of assembling JSON by hand.
3

Create a metadata template and sample data

Create setup_test_data.py to create the sales metadata template, a folder, and a few tagged sample contracts. It reuses get_box_client from the previous step:
Run the script, then copy the printed values into your .env file:
Alternatively, you can point the tutorial at an existing template.The Box Query Insights API can group on enum fields, not free-text string fields:
  • contractType (enum, for example, Sales and Renewal)
  • contractValue (float)
The script is safe to run more than once. It reuses the template and folder, skips uploading a sample file whose name already exists, and falls back to a JSON-Patch update when a file already carries the template. Box Query Insights API reads a metadata index that updates shortly after you tag content. If a metric returns 0 right after setup, wait a minute and run the reporting steps again.
If a sales template already exists whose contractType is a free-text string, the script adds an enum field named contractTypeEnum and prints it as your FIELD_CONTRACT_TYPE. Copy whichever field key the script prints. Grouping on a string field returns 400 Invalid query request.
A Client Credentials Grant app acts as a service account with its own folder tree, so Sales Contracts does not display in your personal All Files. To see it, collaborate yourself on the folder using the service account email from Developer Console > App Details, or open the content as an admin in Content Manager.
4

Count matching contracts

Create reports.py with the queries that power your dashboard. Pass an empty metrics map to get the default totalResultCount metric across all items that match your filter.Every metric result nests its value under values, keyed by the metric type, so metric.values[metric.type] reads the value without hard-coding the type. The remaining steps use the same pattern.
Equivalent cURL request:
The box-version: 2026.0 header is required. The SDK method sets it for you, so you only pass it when you call the endpoint directly.
5

Compute overall contract value statistics

Add this function to reports.py, below count_contracts. It requests multiple metrics in one call to populate summary cards – average, minimum, and maximum deal size for contracts created in a date range.
Because each metric result is keyed by its own type, the same metric.values[metric.type] lookup reads avg, min, and max without a branch per metric. The underlying response looks like this:
See Overall average, minimum, maximum contract value for the full request and response.
6

Group results for dashboard charts

Add this last function to reports.py. Grouping by a metadata field powers bar or pie charts. The API returns the top buckets ordered by document count (descending). Remaining groups are rolled into an other entry.
Only one group_by field is supported per request, and bucket_limit defaults to 5 with a maximum of 10. Group on an enum field such as contractType. A free-text string field returns 400 Invalid query request. Avoid grouping on high-cardinality fields – cardinality of 10,000 or above can cause errors.
7

Combine metrics in a dashboard function

Create dashboard.py to wire the helpers together and print a console report you can adapt for a web dashboard or business intelligence export.

Run and verify

Run the report against your own content to confirm that authentication, the template reference, and response parsing all work.
  1. Confirm your environment values. Your .env file needs the three Client Credentials Grant values plus FOLDER_ID, TEMPLATE_REF, FIELD_CONTRACT_TYPE, and FIELD_CONTRACT_VALUE. The setup script in Create a metadata template and sample data prints the last four values for you.
  2. Run the report. Activate your virtual environment first, then use the command for your path:
    If you built with an agent, check the README it generated for the exact module and run command.
  3. Check the output. With the four sample contracts from the setup step, the console report looks like this:
    Buckets are ordered by document count in descending order, so your group order varies with your own data.
  4. Cross-check one number in Box. Open the contracts folder in Box and count the files that carry your metadata template. That count matches Total contracts from step 3, which confirms your ancestors filter and template reference point at the content you expect.
A metric of 0 right after you tag content usually means the metadata index has not caught up yet. Wait a minute and run the report again. For other symptoms, see Troubleshooting.

Response entry types

Every insight in the response has a type that tells you how to interpret it. For grouped requests, buckets are always sorted by document count in descending order. You cannot customize sort order.

Troubleshooting

See Box Query Insights API guide for the full parameter reference, response fields, and additional examples.

Scaling to production

Dashboard widgets rarely need real-time precision. Refresh aggregates on a schedule, for example, every 15 minutes or hourly, instead of calling the Box Query Insights API on every page load. Store results in your app cache or a data warehouse. The SDK already retries 429 RATE_LIMIT_EXCEEDED and 5xx responses with backoff, so cache to reduce the number of calls rather than adding a retry loop of your own.
Pass department or program folder IDs in ancestors so each team sees only its content. This reduces result set size, keeps dashboards tenant-aware, and aligns with how Box enforces folder-level access for the service account.
Use the Box Query Insights API for summary tiles and charts, then call Box Query API when you select a bucket to load the underlying files. The Box Query Insights API returns aggregates only. Box Query API returns individual items with the same predicate syntax.
Make sure the bucket_limit is set to 10 or less. Prefer low-cardinality dimensions for chart axes. Cardinality of 10,000 or above can cause errors, though that threshold is rarely reached in practice.
Last modified on October 26, 2026