Recipient lists exist and are rarely the real inventory. What you hold is an application database and a suppression list with classification codes attached, and the codes are more informative than any list would be.
Getting the addresses out of SparkPost
The Events API and the suppression list API. SparkPost is built for programmatic access rather than CSV downloads.
Where addresses enter your system, with the suppression list as a reconciliation pass afterwards. Seeding suppressions from old bounce data needs the classification codes read first.
What SparkPost does with the file you bring back
The import rules decide what a re-import actually changes, which is worth knowing before you upload.
The short version
- There is no contact list to import into for transactional sending.
- The suppression list can be managed in bulk through the API, including seeding it from your own bounce history.
- Recipient lists exist for some use cases and upsert on address.
- Subaccounts separate reporting and suppression between sending streams.
The mistake to avoid
Some addresses come back unresolved, and that is a fact about the receiving server rather than a gap in the check. We label those and never bill for them. What that means, and why roughly 28% of business addresses land there, is set out on our page about catch-all domains.
48,000 bounce events, and 21,000 of them are code 25
Suppose you pull ninety days of bounce events from SparkPost and get 48,000 rows. Grouped by bounce class, 17,500 are class 10, 21,000 are class 25, 6,000 fall in the 50s and 3,500 in the 20s. The biggest group is the one to read first.
Class 25 is Admin Failure. SparkPost's documentation explains that once a recipient is on your suppression list, later attempts to deliver to them are rejected with that code. So 21,000 rows aren't 21,000 new problems. They're your application mailing people SparkPost had already suppressed, again and again, because the suppression never made it back to your users table. Count the distinct addresses in those rows and you might find 4,000.
Class 10 is Invalid Recipient, and the docs group it with 30 and 90 as the classes that mean the address shouldn't be retried. Those 17,500 belong in your database with a permanent flag. The 50s are blocks, which the page above already warns you not to suppress.
Transactional and non-transactional are separate switches
A SparkPost suppression entry isn't a single yes or no. Each one records whether it applies to transactional mail, to non-transactional mail, or to both. Someone who unsubscribes from your newsletter should still get a receipt, and the two flags are how that's expressed.
When you load verified-invalid addresses through the suppression API, you choose the flags, and an invalid mailbox should be suppressed for both types. Check what your integration sets by default, because a bulk upload that marks only non-transactional leaves your receipts bouncing.
Also confirm which scope you're writing to. Subaccounts keep their own suppression lists, so an address loaded at the primary account level may not cover a subaccount's sends. SparkPost is part of Bird now, and the documentation has been moving to Bird's domain. If an old bookmark fails, search for the page title instead of trusting a cached copy.
Questions people ask
Where does verification fit with SparkPost?
At the point addresses enter your system. There is no list to clean, so the only durable place is your signup or ingestion path.
Should I pre-load the suppression list?
From invalid recipient bounces, yes. From policy or block bounces, no, because those addresses were never the problem.
Do subaccounts help?
They separate suppression and reporting between streams, which keeps a marketing problem out of your transactional reputation. That is worth setting up before you need it.