# Upgrade your SDK without migrating from usage records Call legacy UsageRecords APIs with RawRequest and a pinned Stripe API version. The [Basil](https://docs.stripe.com/changelog/basil.md) SDK release [deprecated the UsageRecords API and meter-less metered Prices](https://docs.stripe.com/changelog/basil/2025-03-31/deprecate-legacy-usage-based-billing.md). If you need to upgrade your SDK to adopt features unrelated to metered billing, you can continue calling the legacy UsageRecords API by pinning to a pre-Basil API version via `RawRequest`. This lets you upgrade your SDK without disrupting your metered billing integration. This is a temporary workaround, not a migration path. Your subscriptions remain in classic billing mode. To migrate your integration to billing meters, follow the [migration guide](https://docs.stripe.com/billing/subscriptions/usage-based-legacy/migration-guide.md). ### Prerequisites - Your SDK version must support `RawRequest`. - Your Prices have [recurring.usage_type=metered](https://docs.stripe.com/api/prices/object.md#price_object-recurring-usage_type) and no [recurring.meter](https://docs.stripe.com/api/prices/object.md#price_object-recurring-meter) value set. This means that you’re reporting usage through the UsageRecords API, not Billing Meters. ### Report usage ```http POST /v1/subscription_items/{id}/usage_records ``` Use this call to report metered usage for a subscription item. Stripe aggregates these records to calculate what your customer owes at invoice time. Required parameters: `quantity`, `timestamp`, `action` (`set` or `increment`) #### Python ```python client.raw_request( "post", f"/v1/subscription_items/{subscription_item_id}/usage_records", stripe_version="2025-02-24.acacia", quantity=100, timestamp=1234567890, action="increment", ) ``` #### Node.js ```javascript await stripe.rawRequest( 'POST', `/v1/subscription_items/${subscriptionItemId}/usage_records`, { quantity: 100, timestamp: 1234567890, action: 'increment', }, { apiVersion: '2025-02-24.acacia' } ); ``` #### Ruby ```ruby stripe_client.raw_request( :post, "/v1/subscription_items/#{subscription_item_id}/usage_records", params: { quantity: 100, timestamp: 1234567890, action: "increment" }, opts: { stripe_version: "2025-02-24.acacia" } ) ``` #### Go ```go stripe.Key = "sk_test_..." params := &stripe.RawParams{ Params: stripe.Params{ Headers: http.Header{ "Stripe-Version": []string{"2025-02-24.acacia"}, }, }, } result, err := stripe.RawRequest( http.MethodPost, fmt.Sprintf("/v1/subscription_items/%s/usage_records", subscriptionItemID), "quantity=100×tamp=1234567890&action=increment", params, ) ``` #### Java ```java RawRequestOptions options = RawRequestOptions.builder() .setAdditionalHeaders(Map.of("Stripe-Version", "2025-02-24.acacia")) .build(); StripeResponse response = client.rawRequest( ApiResource.RequestMethod.POST, "/v1/subscription_items/" + subscriptionItemId + "/usage_records", "quantity=100×tamp=1234567890&action=increment", options ); ``` #### PHP ```php $response = $stripe->rawRequest( 'post', '/v1/subscription_items/' . $subscriptionItemId . '/usage_records', ['quantity' => 100, 'timestamp' => 1234567890, 'action' => 'increment'], ['stripe_version' => '2025-02-24.acacia'] ); ``` ### Read usage record summaries ```http GET /v1/subscription_items/{id}/usage_record_summaries ``` Use this call to retrieve aggregated usage for a subscription item over a billing period. Useful for displaying current consumption to your customers or reviewing usage before an invoice is finalized. #### Python ```python client.raw_request( "get", f"/v1/subscription_items/{subscription_item_id}/usage_record_summaries", stripe_version="2025-02-24.acacia", ) ``` #### Node.js ```javascript await stripe.rawRequest( 'GET', `/v1/subscription_items/${subscriptionItemId}/usage_record_summaries`, {}, { apiVersion: '2025-02-24.acacia' } ); ``` #### Ruby ```ruby stripe_client.raw_request( :get, "/v1/subscription_items/#{subscription_item_id}/usage_record_summaries", opts: { stripe_version: "2025-02-24.acacia" } ) ``` #### Go ```go stripe.Key = "sk_test_..." params := &stripe.RawParams{ Params: stripe.Params{ Headers: http.Header{ "Stripe-Version": []string{"2025-02-24.acacia"}, }, }, } result, err := stripe.RawRequest( http.MethodGet, fmt.Sprintf("/v1/subscription_items/%s/usage_record_summaries", subscriptionItemID), "", params, ) ``` #### Java ```java RawRequestOptions options = RawRequestOptions.builder() .setAdditionalHeaders(Map.of("Stripe-Version", "2025-02-24.acacia")) .build(); StripeResponse response = client.rawRequest( ApiResource.RequestMethod.GET, "/v1/subscription_items/" + subscriptionItemId + "/usage_record_summaries", "", options ); ``` #### PHP ```php $response = $stripe->rawRequest( 'get', '/v1/subscription_items/' . $subscriptionItemId . '/usage_record_summaries', [], ['stripe_version' => '2025-02-24.acacia'] ); ``` ### Create a Subscription with a legacy metered price ```http POST /v1/subscriptions ``` Use this call to create a subscription that includes a legacy metered price with `recurring.usage_type=metered` and no Billing Meter. Starting with the [Basil API version](https://docs.stripe.com/changelog/basil.md), this request is blocked without a pinned API version. #### Python ```python client.raw_request( "post", "/v1/subscriptions", stripe_version="2025-02-24.acacia", customer="cus_xxx", **{ "items[0][price]": "price_xxx", "payment_behavior": "default_incomplete", "payment_settings[save_default_payment_method]": "on_subscription", } ) ``` #### Node.js ```javascript await stripe.rawRequest( 'POST', '/v1/subscriptions', { customer: 'cus_xxx', 'items[0][price]': 'price_xxx', payment_behavior: 'default_incomplete', 'payment_settings[save_default_payment_method]': 'on_subscription', }, { apiVersion: '2025-02-24.acacia' } ); ``` #### Ruby ```ruby stripe_client.raw_request( :post, "/v1/subscriptions", params: { customer: "cus_xxx", "items[0][price]" => "price_xxx", payment_behavior: "default_incomplete", "payment_settings[save_default_payment_method]" => "on_subscription", }, opts: { stripe_version: "2025-02-24.acacia" } ) ``` #### Go ```go stripe.Key = "sk_test_..." params := &stripe.RawParams{ Params: stripe.Params{ Headers: http.Header{ "Stripe-Version": []string{"2025-02-24.acacia"}, }, }, } body := "customer=cus_xxx&items[0][price]=price_xxx" + "&payment_behavior=default_incomplete" + "&payment_settings[save_default_payment_method]=on_subscription" result, err := stripe.RawRequest(http.MethodPost, "/v1/subscriptions", body, params) ``` #### Java ```java RawRequestOptions options = RawRequestOptions.builder() .setAdditionalHeaders(Map.of("Stripe-Version", "2025-02-24.acacia")) .build(); String body = "customer=cus_xxx&items[0][price]=price_xxx" + "&payment_behavior=default_incomplete" + "&payment_settings[save_default_payment_method]=on_subscription"; StripeResponse response = client.rawRequest( ApiResource.RequestMethod.POST, "/v1/subscriptions", body, options ); ``` #### PHP ```php $response = $stripe->rawRequest( 'post', '/v1/subscriptions', [ 'customer' => 'cus_xxx', 'items[0][price]' => 'price_xxx', 'payment_behavior' => 'default_incomplete', 'payment_settings[save_default_payment_method]' => 'on_subscription', ], ['stripe_version' => '2025-02-24.acacia'] ); ``` ### Update a Subscription with a legacy metered price ```http POST /v1/subscriptions/{id} ``` Use this call to modify an existing subscription that includes a legacy metered price, for example, to add a new metered price item or swap an existing one. This request is blocked on Basil and later without a pinned API version. #### Python ```python client.raw_request( "post", f"/v1/subscriptions/{subscription_id}", stripe_version="2025-02-24.acacia", **{ "items[0][price]": "price_xxx", } ) ``` #### Node.js ```javascript await stripe.rawRequest( 'POST', `/v1/subscriptions/${subscriptionId}`, { 'items[0][price]': 'price_xxx', }, { apiVersion: '2025-02-24.acacia' } ); ``` #### Ruby ```ruby stripe_client.raw_request( :post, "/v1/subscriptions/#{subscription_id}", params: { "items[0][price]" => "price_xxx" }, opts: { stripe_version: "2025-02-24.acacia" } ) ``` #### Go ```go stripe.Key = "sk_test_..." params := &stripe.RawParams{ Params: stripe.Params{ Headers: http.Header{ "Stripe-Version": []string{"2025-02-24.acacia"}, }, }, } result, err := stripe.RawRequest( http.MethodPost, fmt.Sprintf("/v1/subscriptions/%s", subscriptionID), "items[0][price]=price_xxx", params, ) ``` #### Java ```java RawRequestOptions options = RawRequestOptions.builder() .setAdditionalHeaders(Map.of("Stripe-Version", "2025-02-24.acacia")) .build(); StripeResponse response = client.rawRequest( ApiResource.RequestMethod.POST, "/v1/subscriptions/" + subscriptionId, "items[0][price]=price_xxx", options ); ``` #### PHP ```php $response = $stripe->rawRequest( 'post', '/v1/subscriptions/' . $subscriptionId, ['items[0][price]' => 'price_xxx'], ['stripe_version' => '2025-02-24.acacia'] ); ```