--- title: Enhanced Reports API Integration | PostGrid description: Access PostGrid's enhanced reports via the API to run ad-hoc SQL queries, schedule saved reports, and export mail analytics data programmatically. --- View advanced built-in reports and create your own without any additional infrastructure. One of PostGrid’s main advantages over traditional direct mail companies is **visibility**. You’re able to monitor the status of each individual mailer, get insights into its delivery, and export that data whenever you need it. Enhanced reports extend PostGrid’s capabilities to provide **more detailed analytics** (e.g. delivery velocity, mail categories, etc) and to let you **create your own custom reports**. Not only that, you can access these reports using our API as well as our dashboard, allowing you to build automations on top of them. Reach out to to get access. ## Ad-Hoc Reports One way to use PostGrid’s enhanced reports system is to run ad-hoc *SQL queries* against your data lake. This lets get quick insights into your data without setting up a report. Here’s a query that gets the `id` and `status` of all letters sent to a contact with a particular first name: ``` select l.id, l.status from letters l join contacts c on c.id = l.to_id where c.firstName = ? ``` Let’s run this query using the API to find all the letters sent to `Kevin` ([API Reference](/api/index.md)) Terminal window ``` curl --location 'https://api.postgrid.com/print-mail/v1/reports/samples' \ --header 'x-api-key: {YOUR_API_KEY}' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'sqlQuery=select l.id, l.status from letters l join contacts c on c.id = l.to_id where c.firstName = ?' \ --data-urlencode 'params[]=Kevin' \ --data-urlencode 'limit=5' ``` Responds with ``` { "id": "report_sample_qWvJDT7PCtpSw499WQfBx3", "object": "report_sample", "records": [ { "id": "letter_bYuofk7EVPupccaMqitzG8", "status": "completed" }, { "id": "letter_qJD9FaoHCrLuW9AsmtnMBP", "status": "completed" }, { "id": "letter_44su5CvLkWQPd1ptoBHK4J", "status": "completed" }, { "id": "letter_cezhbzY9s4Yk9art23f5gD", "status": "completed" }, { "id": "letter_4d4fnfFTb1SU1QgNrSaJvq", "status": "completed" } ] } ``` These are letter IDs and statuses for letters that are sent to somebody with the first name `Kevin`. We’re passing `Kevin` in as the first binding to the SQL query via `params`, preventing any SQL injection. This means you can safely pass untrusted data into params – e.g. letting your users query for all of *their* letters by first name. Whilst the query that I’ve shown above could be replicated using PostGrid’s [structured search API](https://docs.postgrid.com/#search) , you can perform much more advanced queries using SQL. Here’s another example where we get the average page count of all our letters ([API Reference](/api/index.md)) Terminal window ``` curl --location 'https://api.postgrid.com/print-mail/v1/reports/samples' \ --header 'x-api-key: {YOUR_API_KEY}' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'sqlQuery=select avg(pageCount) from letters' ``` Responds with ``` { "id": "report_sample_rsmfyTfMPZSsNGBKP9wCAG", "object": "report_sample", "records": [ { "avg(pageCount)": 2.1316224253772442 } ] } ``` These are just some examples of what you can do with ad-hoc SQL queries. You have access to all the querying capabilities of DuckDB as documented [here](https://duckdb.org/docs/stable/sql/introduction) . ### Limitations These ad-hoc reports can only run for a maximum of 30s and return at most 1000 rows (you can specify at most `1000` for `limit`). Also, you can only at most run 2 queries per second. Please reach out to if you would like a higher quota on any of these values. Also, the data provided in these reports may be up to 2 hours behind your latest PostGrid data. If you need real-time data, we recommend listening to our webhooks or polling our `GET` endpoints. ## Saved Reports Once you’ve sampled a query, you may want to save it for future use, or you may need more than just 1000 rows of data. You can create a saved report by hitting the `POST /reports` endpoint ([API Reference](/api/index.md)) Terminal window ``` curl --location 'https://api.postgrid.com/print-mail/v1/reports' \ --header 'x-api-key: {YOUR_API_KEY}' \ --header 'Content-Type: application/json' \ --data '{ "sqlQuery": "select * from letters where sendDate > ?::TIMESTAMP and sendDate < ?::TIMESTAMP" }' ``` This query simply gives me back all the letters which have a `sendDate` within a given time range. This gives me back a report ID and other details ``` { "id": "report_8BriPzDkDChoUDZuBYcwb8", "object": "report", "live": false, "sqlQuery": "select * from letters where sendDate > ?::TIMESTAMP and sendDate < ?::TIMESTAMP", "createdAt": "2025-11-11T19:38:15.819Z", "updatedAt": "2025-11-11T19:38:15.819Z" } ``` Now, I can sample this report by hitting `POST /reports/report_8BriPzDkDChoUDZuBYcwb8/samples` same as before. ([API Reference](/api/index.md)) I can also initiate a report *export* which lets me export up to 100 **megabytes** of data from PostGrid ([API Reference](/api/index.md)) Terminal window ``` curl --location 'https://api.postgrid.com/print-mail/v1/reports/report_8BriPzDkDChoUDZuBYcwb8/exports' \ --header 'x-api-key: {YOUR_API_KEY}' \ --header 'Content-Type: application/json' \ --data '{ "params": ["2025-04-01", "2025-04-30"] }' ``` Which responds with ``` { "id": "report_export_3cyBHymXp6R2HLiHJjWpDn", "object": "report_export", "live": false, "params": ["2025-04-01", "2025-04-30"], "report": { "id": "report_cfUj47hyjWfoQP9boGFKsL", "sqlQuery": "select * from letters where sendDate > ?::TIMESTAMP and sendDate < ?::TIMESTAMP" }, "createdAt": "2025-11-11T19:41:17.114Z", "updatedAt": "2025-11-11T19:41:17.114Z" } ``` I can then poll this for completion by hitting the `GET /reports/report_cfUj47hyjWfoQP9boGFKsL/exports/report_export_3cyBHymXp6R2HLiHJjWpDn` endpoint ([API Reference](/api/index.md)) ``` { "id": "report_export_3cyBHymXp6R2HLiHJjWpDn", "object": "report_export", "live": false, "outputURL": "https://pg-prod-bucket-1.s3.amazonaws.com/report-exports/test/org_aSFrdfG2QGnS6vzx3BSAZt/report_export_3cyBHymXp6R2HLiHJjWpDn.csv?AWSAccessKeyId=AKIA5GFUILSULWTWCR64&Expires=1762891057&Signature=pqfy8%2B%2FGR7idaaiToTqYtsaVHDI%3D", "params": ["2025-04-01", "2025-04-30"], "report": { "id": "report_cfUj47hyjWfoQP9boGFKsL", "sqlQuery": "select * from letters where sendDate > ?::TIMESTAMP and sendDate < ?::TIMESTAMP" }, "rowCount": 186, "sizeInBytes": 106413, "createdAt": "2025-11-11T19:41:17.114Z", "updatedAt": "2025-11-11T19:41:22.621Z" } ``` Once there is an `outputURL` populated, I can use this link to download the CSV file with all my data. ### Limitations As mentioned before, you can only export up to 100mb of data in that CSV file. PostGrid will truncate anything past that point. Moreover, your export can only be processed for a maximum of *10 minutes*. Poorly structured queries that process 100s of millions of orders are likely to exceed this limit, so leverage parameters to scope down your queries as needed.