When the Microsoft Advertising API is worth the build
Most agencies who build against this API did not need to. Most who needed to waited two years longer than they should have.
The Microsoft Advertising API is well documented and genuinely usable, which makes it easy to build something you did not need. The decision turns on a small number of things: how many accounts you touch, whether the work is reporting or writing, and whether anyone will maintain it in eighteen months. There is also a hard platform date now in play, because the SOAP interface has a published retirement schedule, and that changes the arithmetic for anyone already integrated.
The three tiers, and most teams belong in the middle
| Tier | Fits when | Real cost |
|---|---|---|
| Interface and editor | One to a handful of accounts, changes measured in dozens | Time, and no artefact anyone can review |
| Bulk files | Estate-scale edits, repeatable, reviewable before upload | A spreadsheet discipline and a naming convention |
| API integration | Scheduled work, many accounts, or a system of record elsewhere | Auth, throttling, retries, schema drift, and an owner |
The most common mistake is jumping from the first tier to the third because the second is unglamorous. Bulk files handle far more than people credit them for. Microsoft's own guidance is that the bulk service is recommended for managing large scale data, especially when adding or updating ads and keywords across multiple ad groups or campaigns, and the campaign service documentation notes that a single call there updates at most 100 ad groups, all of which must sit in the same campaign.
So the question is not whether you have a lot of changes. It is whether the changes need to happen without a person present. A quarterly restructure across forty campaigns is a bulk file. A nightly reconciliation between the ad platform and a CRM is an API build.
My threshold with a small team is roughly this: if the same task will run unattended more than fifty times, or if it has to run at a time nobody is awake, build it. Otherwise write a good bulk file process and spend the engineering budget somewhere it compounds. The tradeoff is the same one in choosing between platform scripts and a real automation build.
The platform date that forces a decision
This is now the largest single input to the decision, and it is not a judgement call. Microsoft's migration guidance states that starting 1 October 2026, all new Microsoft Advertising API features and enhancements will be available only through the REST API, that existing SOAP integrations continue to run during a six month migration window, and that the SOAP API is scheduled for full deprecation on 31 January 2027.
The same page sets out the SDK positions: .NET and Java users below version 13.0.22 should upgrade, and Python and PHP users on the bingads package should move to the msads package. Teams with no SDK have to change their URLs and request format directly.
For anyone deciding whether to build now, that timeline removes an option rather than adding one. There is no reason to write new SOAP code. For anyone already integrated, the migration is not optional maintenance, it is a dated project with a deadline, and Microsoft notes that customers with complex or large scale integrations can request an extended timeline through support.
The lesson generalises past this one migration. An integration is a liability with a maintenance schedule attached, and the schedule is set by the vendor rather than by you. If nobody on the team owns that liability, the honest answer to whether you should build is no.
What you have to handle that a demo never shows
Authentication is per user and per token type. The getting started guide explains that you need a developer token and user credentials, and that the universal developer token can be used to authenticate with any Microsoft Advertising user credentials, which has been the default token type since July 2019. It is explicit that a developer token grants no additional account permissions: the accounts available to a user in the interface are the accounts available through the API, no more.
Throttling is documented per service and you will meet it. The services protocol guide sets out the errors: error 117, CallRateExceeded, on the Ad Insight and Campaign Management services with a sliding sixty second window; error 4204 on the bulk service, where you can resubmit after waiting up to fifteen minutes; and error 207, ConcurrentRequestOverLimit, on reporting, where you wait for previous reports to finish.
The guide is candid that the exact limits are internal and subject to change, which is the important part. You cannot pin your retry logic to a published number, so it has to back off on the error rather than count requests. This is the same design problem as handling rate limits during a backfill, and the same answer applies: respect the error, not your model of the limit.
Partial success is the semantic that catches people. Add, update and delete operations support partial completion, so if one object in a list fails the operation may succeed for the others. On a get operation with a list of identifiers, an invalid identifier does not fail the call, it returns nil in the corresponding response position. Code that checks only the operation result and not the per-item results will report success on a batch that half worked.
And partial update is not uniform. The guide states that partial update is supported for most campaign management objects but not for ad extensions, where any optional element not sent with the update request is in effect deleted. The customer management service performs a full update, so you must supply every optional property you do not want nulled.
Test properly, which means understanding the sandbox
There is a sandbox and it has real constraints. The sandbox guide states that ads created there are not served, that sandbox and production use separate credentials, and that everyone can use the universal sandbox developer token.
The constraint that surprises people is the account requirement: sandbox supports Microsoft accounts created with an outlook.com email address only, and invitations may only be sent to outlook.com addresses. The documentation warns that an invite sent to any other domain will show as pending indefinitely and is never actually sent.
Volume differs too. The bulk service limit in production is 100MB and up to four million rows, and in the sandbox it is twenty thousand rows. So a sandbox test of a large bulk operation is testing the code path, not the scale, and the scale is usually where the failure lives.
None of this is a reason not to test. It is a reason to know what your test proved. A green sandbox run tells you the request shape is right and tells you nothing about throttling behaviour under real concurrency.
The decision, stated plainly
Build if the work must run unattended, if the number of accounts makes a manual pass genuinely infeasible, or if the ad platform has to exchange data with a system of record that will not accept a spreadsheet. Those are the three cases I have seen justify the cost.
Do not build to save an afternoon a month, to look more technical in a pitch, or because a client asked whether you have an API integration. Those produce systems that work for a year and then break on a schema change nobody was watching for.
If you do build, put it in version control with the same seriousness as any other production code, because it writes to something that spends money. That argument is not specific to this platform and it is made properly in version control for analytics and automation.
And be honest about the maintenance owner before the first line is written. On the clinical research automation programme, the value was not the automation itself, it was that 159 work items were scoped, sequenced and made trackable so somebody could actually run them. An integration without an owner is not an asset. It is a future incident with a nice architecture diagram, and getting to the point where it has a named owner is exactly what a fractional technical project management engagement is for.