# 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&timestamp=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&timestamp=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']
);
```
