MIGRATE KEAP FROM XML-RPC TO REST v2 BEFORE THE SUNSET
Keap is turning off its oldest API. If anything you run touches Keap through XML-RPC — and you might not know it does — it stops on the sunset date. Here is what actually changes, and the one opt-in detail that quietly makes migrated contacts non-marketable.
THE DEADLINE, AND WHY IT IS FUZZIER THAN IT SHOULD BE
Keap announced it is retiring the XML-RPC API and moving everyone to REST v2. The officially announced date is December 31, 2026 — after which XML-RPC calls stop, with no grace period. Through 2026 there are scheduled brownouts: short, deliberate outages of XML-RPC that get longer as the year goes on, so your integration fails in a controlled window instead of failing for real on New Year's Day.
The date has been muddier than a hard deadline should be. Some partners received emails in 2026 citing a 2027 sunset; Keap called that an error. Meanwhile there is real pressure to push the date, because REST v2 still has missing pieces developers keep hitting mid-migration. None of that is a reason to relax.
Plan against December 31, 2026. But before you commit a client to a hard date, confirm the current one against Keap's own XML-RPC sunset article and the brownout schedule. A date hardcoded into a guide is exactly the thing that goes stale.
FIRST, FIND OUT IF THIS EVEN TOUCHES YOU
The people most exposed here are the ones who do not think they are. XML-RPC is old enough that it is buried inside things — a Zapier connection, a plugin a developer installed years ago, a script nobody has looked at since it started working. The owner has no idea any of it speaks XML-RPC, because it just quietly runs.
So the first job is inventory, not code. Two passes:
- Your own code. Grep the codebase for the XML-RPC endpoint and the old service-method names. Anything posting to
/crm/xmlrpc/v1is on the clock. - Everything else wired to Keap. Every third-party tool, every automation, every integration someone set up and forgot. If you cannot say for certain what an integration speaks, assume XML-RPC until you have proven otherwise.
Keap also publishes an integrator status report so you can see whether your own app is still sending XML-RPC traffic. Start there before you assume you are clear.
WHAT ACTUALLY CHANGES
This is not a version bump. XML-RPC and REST v2 are different shapes, and four things change at once.
- Transport. XML over HTTP becomes JSON over HTTP. Lighter, and every language you would use already speaks it natively.
- Structure. Method calls become resources. You stop invoking
ContactService.addand start POSTing to/v2/contacts. Verbs (GET, POST, PATCH, DELETE) do what the method name used to. - Auth. This is the big one. Legacy keys become OAuth 2.0 — a short-lived access token plus a refresh token you use to get new ones. See Keap's OAuth guide.
- Errors. Generic XML faults become real HTTP status codes with structured JSON bodies, so you can finally branch on what went wrong.
// XML-RPC (before): one endpoint, method named in the body
POST /crm/xmlrpc/v1
method: ContactService.add
params: [ apiKey, { FirstName, Email } ]
// REST v2 (after): a resource, a verb, an OAuth bearer token
POST /crm/rest/v2/contacts
Authorization: Bearer <access_token>
{ "given_name": "Jane", "email_addresses": [ ... ] }
The base URL moves from api.infusionsoft.com/crm/xmlrpc/v1 to api.infusionsoft.com/crm/rest/v2, and the full parameter and response shapes for every endpoint live in the REST v2 documentation.
THE MAPPING IS NOT ONE-TO-ONE
Most XML-RPC methods have a clean REST v2 equivalent, and Keap publishes a method-to-endpoint mapping that saves you the guesswork. Use it. But do not assume the mapping is total.
A set of XML-RPC methods are not being converted at all, and as of mid-2026, developers migrating real integrations were still finding gaps and limitations as they went. The practical consequence: check the gap list before you scope the work, not during it. If a method you depend on has no REST v2 home yet, that is a sequencing decision to make on day one — flag it, raise it in the developer forum, and do the calls that do map first while the gap gets resolved.
THE GOTCHA THAT MAKES CONTACTS NON-MARKETABLE
This is the one that cost us time, so it is the one worth handing you. When you create or update a contact in REST v2 and you set an opt-in reason, where you put it matters more than what you put.
The XML-RPC-era instinct is to treat the opt-in as a property of the contact. In REST v2 the email address is its own object inside an email_addresses array, and the opt-in reason has to be nested inside that email object. Put it at the top level and the request still succeeds — the contact saves, you get a 200, nothing looks wrong. But the contact lands non-marketable, which means every campaign and broadcast silently skips it. You do not find out from an error. You find out weeks later when a send goes to fewer people than it should have.
// WRONG — opt_in_reason at the top level (the v1 habit).
// Saves fine, returns 200, lands NON-MARKETABLE.
{
"given_name": "Jane",
"email_addresses": [{ "email": "jane@acme.com", "field": "EMAIL1" }],
"opt_in_reason": "Imported from prior system"
}
// RIGHT — opt_in_reason nested INSIDE the email object.
{
"given_name": "Jane",
"email_addresses": [{
"email": "jane@acme.com",
"field": "EMAIL1",
"opt_in_reason": "Imported from prior system"
}]
}
Why it bites: the failure is silent. A non-marketable contact is not an error you can catch in code — it is a state you have to go looking for. If you are migrating a contact import, spot-check the marketable status of the first batch before you run the other ten thousand.
HANDLE THE ERRORS REST ACTUALLY GIVES YOU
XML-RPC handed back generic faults, so most integrations treated every failure the same way. REST v2 returns real HTTP status codes, which means you can — and should — branch on them:
- 401 — the access token expired. Refresh it and retry, do not fail the job.
- 429 — you are being rate-limited. Back off and retry, do not hammer.
- 400 — your payload is wrong. Fix the request, retrying will not help.
- 403 / 404 — permissions or a bad id. A retry is pointless; log and surface it.
The single highest-value thing to centralize is token handling: refresh in one place, so a 401 anywhere in your integration heals itself instead of taking down whichever call happened to hit the expiry.
VALIDATE AGAINST THE OLD CALLS, NOT AGAINST HOPE
The migration is done when the new calls return the same information the old ones did — not when they return something. Keep the XML-RPC responses around and compare field by field: are the mandatory fields populated, are dates and numbers in the format REST expects, did anything the old call returned quietly drop out of the new one.
And use the brownouts on purpose. They are free production-condition tests: during a brownout, XML-RPC is down, so anything that breaks is something you still have not migrated. That is the cheapest possible way to discover a dependency you missed — far cheaper than discovering it on January 1.
USE THE SDKS, OR AT LEAST KNOW THEY EXIST
Keap maintains official SDKs generated from the REST v2 OpenAPI spec, in several languages, in the keap-sdk repository. If your stack matches one, they handle a lot of the request and response plumbing for you. If you would rather use a plain HTTP client, that is fine too — you just own the headers, pagination, and error handling yourself. Either way, knowing the SDKs exist beats reinventing them by accident.
COMMON QUESTIONS
The officially announced sunset is December 31, 2026, with brownouts throughout 2026 and the final one in early December. Some 2027 emails went out and were called an error, and there is community pressure to extend the date because REST v2 still has gaps — so plan against December 31, 2026 and verify the current date before betting a deadline on it.
Search your code for the /crm/xmlrpc/v1 endpoint and audit every third-party tool wired to Keap. Most owners who use it use it indirectly — through a Zapier connection or an old plugin — and have no idea. If you cannot account for an integration, assume it is affected until proven otherwise.
No. Both APIs run in parallel until the sunset, so migrate one call at a time: move it, validate it against the old response, ship it, repeat. A staged migration is lower-risk than a big-bang rewrite, and there is no reason to take the bigger risk.
Because opt_in_reason was sent at the top level of the contact instead of nested inside the email address object. The request succeeds, but the contact lands non-marketable and campaigns silently skip it. Nest the opt-in inside the email object and spot-check marketable status before a bulk import.
A small set of methods are not being converted, and gaps were still surfacing mid-2026. Check that list before scoping, do the calls that map first, and flag anything with no equivalent in the developer forum rather than discovering it at the deadline.
We build and maintain Keap integrations for a living, and we have already run this migration — including the parts that fail silently. If you would rather hand it off, or just want a second set of eyes before the sunset, that is what the consult is for.
Book a Free Consult