Change plan, interval or add-on
using System.Net.Http.Headers;var client = new HttpClient();var request = new HttpRequestMessage{ Method = HttpMethod.Post, RequestUri = new Uri("https://oddslink.app/v1/billing/change"), Headers = { { "Authorization", "Bearer <token>" }, }, Content = new StringContent("{ \"plan\": \"api_scale\", \"interval\": \"month\" }") { Headers = { ContentType = new MediaTypeHeaderValue("application/json") } }};using (var response = await client.SendAsync(request)){ response.EnsureSuccessStatusCode(); var body = await response.Content.ReadAsStringAsync(); Console.WriteLine(body);}package main
import ( "fmt" "strings" "net/http" "io")
func main() {
url := "https://oddslink.app/v1/billing/change"
payload := strings.NewReader("{ \"plan\": \"api_scale\", \"interval\": \"month\" }")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>") req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close() body, _ := io.ReadAll(res.Body)
fmt.Println(res) fmt.Println(string(body))
}OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/json");RequestBody body = RequestBody.create(mediaType, "{ \"plan\": \"api_scale\", \"interval\": \"month\" }");Request request = new Request.Builder() .url("https://oddslink.app/v1/billing/change") .post(body) .addHeader("Authorization", "Bearer <token>") .addHeader("Content-Type", "application/json") .build();
Response response = client.newCall(request).execute();const url = 'https://oddslink.app/v1/billing/change';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"plan":"api_scale","interval":"month"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://oddslink.app/v1/billing/change \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "plan": "api_scale", "interval": "month" }'Signed-in website session only; the body is the same as checkout. A bigger plan, or a move from monthly to yearly, is charged the difference to the saved card now and takes effect at once (charged_cents is what was charged). A smaller plan, a move from yearly to monthly, or turning the add-on off waits for the next renewal and shows up as pending_change, with charged_cents 0. A subscription whose last renewal failed must have its card updated first.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”object
The plan. api_free is not accepted, because it costs nothing.
Billing period. Yearly is the same ladder at a 20% discount.
Include the live-feed add-on. Accepted only with api_growth and api_scale.
Examples
Move to Scale, billed monthly
{ "plan": "api_scale", "interval": "month"}Responses
Section titled “ Responses ”The subscription after the change, and what was charged now.
object
An account’s subscription. The card is described only by its last four digits.
object
A plan slug, for example api_growth. New plans may be added.
month or year.
Whether the live-feed add-on is included.
active, or past_due when the last renewal failed and the card must be updated.
A change that takes effect at the next renewal.
object
A plan slug, for example api_growth.
month or year.
What one renewal charges, in cents.
Cents charged to the saved card by this change. 0 when the change waits for the renewal.
Example
{ "subscription": { "plan": "api_growth", "interval": "year", "addon": false, "status": "active", "current_period_start": "2026-09-30T00:00:00.000Z", "current_period_end": "2027-09-30T00:00:00.000Z", "cancel_at_period_end": false, "pending_change": null, "card_last4": "4242", "amount_cents": 47000, "next_charge_at": "2027-09-30T00:00:00.000Z" }, "charged_cents": 0}Headers
Section titled “Headers”Requests used this billing period.
Requests remaining this billing period.
What this one call cost against the allowance (0 for a free call).
This plan’s speed limit, in requests per second.
Requests left in the current one-second window.
Seconds until the speed-limit window resets.
This request’s id, for support and for matching against error.request_id.
A parameter was malformed.
object
object
Stored Unix-second choices on AMBIGUOUS_EVENT or INVALID_STORED_COMMENCE_TIME; independent of dateFormat.
Valid case-sensitive book keys on INVALID_BOOKMAKER.
403 only: the plan on the presented key.
403 only: the lowest plan that includes this request. For history, that depends on how long ago the fixture started as well as on the endpoint.
Example
{ "error": { "code": "BAD_REQUEST", "message": "commenceTimeFrom is not a valid ISO-8601 timestamp.", "docs_url": "https://oddslink.app/docs/errors/#bad-request", "request_id": "req_01hz8k3z9e2v6w1x" }}Headers
Section titled “Headers”Requests used this billing period.
Requests remaining this billing period.
What this one call cost against the allowance (0 for a free call).
This plan’s speed limit, in requests per second.
Requests left in the current one-second window.
Seconds until the speed-limit window resets.
This request’s id, for support and for matching against error.request_id.
The Authorization bearer session is missing or invalid.
object
object
Stored Unix-second choices on AMBIGUOUS_EVENT or INVALID_STORED_COMMENCE_TIME; independent of dateFormat.
Valid case-sensitive book keys on INVALID_BOOKMAKER.
403 only: the plan on the presented key.
403 only: the lowest plan that includes this request. For history, that depends on how long ago the fixture started as well as on the endpoint.
Examples
No Authorization: Bearer header sent
{ "error": { "code": "MISSING_SESSION", "message": "Sign in and try again.", "docs_url": "https://oddslink.app/docs/errors/#missing-session", "request_id": "req_01hz8k4b9h5y0z1a" }}Session is expired or invalid
{ "error": { "code": "INVALID_SESSION", "message": "Your session has expired. Sign in again.", "docs_url": "https://oddslink.app/docs/errors/#invalid-session", "request_id": "req_01hz8k4c0j6z1a2b" }}Headers
Section titled “Headers”Requests used this billing period.
Requests remaining this billing period.
What this one call cost against the allowance (0 for a free call).
This plan’s speed limit, in requests per second.
Requests left in the current one-second window.
Seconds until the speed-limit window resets.
This request’s id, for support and for matching against error.request_id.
The saved card was declined. Nothing was changed.
object
object
Stored Unix-second choices on AMBIGUOUS_EVENT or INVALID_STORED_COMMENCE_TIME; independent of dateFormat.
Valid case-sensitive book keys on INVALID_BOOKMAKER.
403 only: the plan on the presented key.
403 only: the lowest plan that includes this request. For history, that depends on how long ago the fixture started as well as on the endpoint.
Example
{ "error": { "code": "PAYMENT_DECLINED", "message": "the card was declined; nothing was changed", "docs_url": "https://oddslink.app/docs/errors/#payment-declined", "request_id": "req_01J8Z5Q2K9V3N7M4" }}Headers
Section titled “Headers”Requests used this billing period.
Requests remaining this billing period.
What this one call cost against the allowance (0 for a free call).
This plan’s speed limit, in requests per second.
Requests left in the current one-second window.
Seconds until the speed-limit window resets.
This request’s id, for support and for matching against error.request_id.
This account has no subscription.
object
object
Stored Unix-second choices on AMBIGUOUS_EVENT or INVALID_STORED_COMMENCE_TIME; independent of dateFormat.
Valid case-sensitive book keys on INVALID_BOOKMAKER.
403 only: the plan on the presented key.
403 only: the lowest plan that includes this request. For history, that depends on how long ago the fixture started as well as on the endpoint.
Example
{ "error": { "code": "NO_SUBSCRIPTION", "message": "this account has no subscription", "docs_url": "https://oddslink.app/docs/errors/#no-subscription", "request_id": "req_01J8Z5Q2K9V3N7M4" }}Headers
Section titled “Headers”Requests used this billing period.
Requests remaining this billing period.
What this one call cost against the allowance (0 for a free call).
This plan’s speed limit, in requests per second.
Requests left in the current one-second window.
Seconds until the speed-limit window resets.
This request’s id, for support and for matching against error.request_id.
The last renewal failed (PAYMENT_PAST_DUE, update the card first), or another billing change is in progress (BILLING_BUSY, try again shortly).
object
object
Stored Unix-second choices on AMBIGUOUS_EVENT or INVALID_STORED_COMMENCE_TIME; independent of dateFormat.
Valid case-sensitive book keys on INVALID_BOOKMAKER.
403 only: the plan on the presented key.
403 only: the lowest plan that includes this request. For history, that depends on how long ago the fixture started as well as on the endpoint.
Examples
The last renewal failed
{ "error": { "code": "PAYMENT_PAST_DUE", "message": "update your card before changing plan", "docs_url": "https://oddslink.app/docs/errors/#payment-past-due", "request_id": "req_01J8Z5Q2K9V3N7M4" }}Another change is in progress
{ "error": { "code": "BILLING_BUSY", "message": "a billing change is already in progress; try again", "docs_url": "https://oddslink.app/docs/errors/#billing-busy", "request_id": "req_01J8Z5Q2K9V3N7M4" }}Headers
Section titled “Headers”Requests used this billing period.
Requests remaining this billing period.
What this one call cost against the allowance (0 for a free call).
This plan’s speed limit, in requests per second.
Requests left in the current one-second window.
Seconds until the speed-limit window resets.
This request’s id, for support and for matching against error.request_id.
The plan’s speed limit was exceeded; wait Retry-After seconds. A call that does not count against the allowance is never refused for a spent allowance.
object
object
Stored Unix-second choices on AMBIGUOUS_EVENT or INVALID_STORED_COMMENCE_TIME; independent of dateFormat.
Valid case-sensitive book keys on INVALID_BOOKMAKER.
403 only: the plan on the presented key.
403 only: the lowest plan that includes this request. For history, that depends on how long ago the fixture started as well as on the endpoint.
Example
{ "error": { "code": "SPEED_LIMIT", "message": "Speed limit exceeded: 10 requests/second on the Growth plan.", "docs_url": "https://oddslink.app/docs/errors/#speed-limit", "request_id": "req_01hz8k4f7m9c3d8f" }}Headers
Section titled “Headers”Requests used this billing period.
Requests remaining this billing period.
What this one call cost against the allowance (0 for a free call).
This plan’s speed limit, in requests per second.
Requests left in the current one-second window.
Seconds until the speed-limit window resets.
This request’s id, for support and for matching against error.request_id.
Seconds to wait before retrying on 429 SPEED_LIMIT or 503 ODDS_UNAVAILABLE.
The billing provider could not be reached.
object
object
Stored Unix-second choices on AMBIGUOUS_EVENT or INVALID_STORED_COMMENCE_TIME; independent of dateFormat.
Valid case-sensitive book keys on INVALID_BOOKMAKER.
403 only: the plan on the presented key.
403 only: the lowest plan that includes this request. For history, that depends on how long ago the fixture started as well as on the endpoint.
Example
{ "error": { "code": "BILLING_UNAVAILABLE", "message": "could not reach the billing provider; try again", "docs_url": "https://oddslink.app/docs/errors/#billing-unavailable", "request_id": "req_01J8Z5Q2K9V3N7M4" }}Headers
Section titled “Headers”Requests used this billing period.
Requests remaining this billing period.
What this one call cost against the allowance (0 for a free call).
This plan’s speed limit, in requests per second.
Requests left in the current one-second window.
Seconds until the speed-limit window resets.
This request’s id, for support and for matching against error.request_id.
Billing is not configured on this deployment. Fails closed rather than taking money it cannot then account for.
object
object
Stored Unix-second choices on AMBIGUOUS_EVENT or INVALID_STORED_COMMENCE_TIME; independent of dateFormat.
Valid case-sensitive book keys on INVALID_BOOKMAKER.
403 only: the plan on the presented key.
403 only: the lowest plan that includes this request. For history, that depends on how long ago the fixture started as well as on the endpoint.
Example
{ "error": { "code": "BILLING_NOT_CONFIGURED", "message": "billing is not configured on this deployment", "docs_url": "https://oddslink.app/docs/errors/#billing-not-configured", "request_id": "req_01J8Z5Q2K9V3N7M4" }}Headers
Section titled “Headers”Requests used this billing period.
Requests remaining this billing period.
What this one call cost against the allowance (0 for a free call).
This plan’s speed limit, in requests per second.
Requests left in the current one-second window.
Seconds until the speed-limit window resets.
This request’s id, for support and for matching against error.request_id.
Something failed on our side.
object
object
Stored Unix-second choices on AMBIGUOUS_EVENT or INVALID_STORED_COMMENCE_TIME; independent of dateFormat.
Valid case-sensitive book keys on INVALID_BOOKMAKER.
403 only: the plan on the presented key.
403 only: the lowest plan that includes this request. For history, that depends on how long ago the fixture started as well as on the endpoint.
Example
{ "error": { "code": "INTERNAL_ERROR", "message": "Something went wrong on our side. It has been logged.", "docs_url": "https://oddslink.app/docs/errors/#internal-error", "request_id": "req_01hz8k4h8n0d4e9f" }}Headers
Section titled “Headers”Requests used this billing period.
Requests remaining this billing period.
What this one call cost against the allowance (0 for a free call).
This plan’s speed limit, in requests per second.
Requests left in the current one-second window.
Seconds until the speed-limit window resets.
This request’s id, for support and for matching against error.request_id.