Get Products, Offerings & Prices with RevenueCat
Overview
To show a paywall, you need to load the products you sell and display their prices. RevenueCat gives
you two ways to do this: Offerings (the recommended path) and raw products
fetched by id. An Offering is a set of Package objects you configure in the
RevenueCat dashboard, and each package wraps a StoreProduct with a localized
priceString.
The two APIs are:
getOfferings(): returns the offerings (and their packages) configured in the dashboard. Prefer this so you can change products without an app update.getProducts([ids]): returns specificStoreProductobjects by store identifier. Use this only when you need a particular product by id.
getProducts only when you genuinely need a specific
product by its store id.
configure(...). See the Configure the SDK guide and the
platform tutorials (React Native, iOS,
Android, Flutter) for setup.
Get the Current Offering
Call getOfferings() and read the current offering. The current offering is the
one you marked as default in the dashboard. Its availablePackages array holds the packages
you can present on a paywall.
// React Native (preferred): load the current offering
import Purchases from 'react-native-purchases';
const offerings = await Purchases.getOfferings();
const current = offerings.current;
if (current && current.availablePackages.length > 0) {
const pkg = current.availablePackages[0];
const priceLabel = pkg.product.priceString; // already localized, e.g. "$9.99"
console.log('First package price:', priceLabel);
}
The same idea on each native platform:
// iOS (Swift): load the current offering
let offerings = try await Purchases.shared.offerings()
if let pkg = offerings.current?.availablePackages.first {
let label = pkg.storeProduct.localizedPriceString // e.g. "$9.99"
print("First package price:", label)
}
// Android (Kotlin): load the current offering
val offerings = Purchases.sharedInstance.awaitOfferings()
val pkg = offerings.current?.availablePackages?.firstOrNull()
val label = pkg?.product?.price?.formatted // localized price string, e.g. "$9.99"
println("First package price: $label")
// Flutter (Dart): load the current offering
Offerings offerings = await Purchases.getOfferings();
final pkg = offerings.current?.availablePackages.first;
final label = pkg?.storeProduct.priceString; // localized price string, e.g. "$9.99"
print('First package price: $label');
offerings.current will be null and
availablePackages can be empty. Always guard for these cases before reading a package.
Read the Package & Product Price
Each Package wraps a StoreProduct. The product holds the localized price
string you should render directly in your UI. Below, the full list of packages is mapped to display
values:
// React Native: build display rows for every package
const offerings = await Purchases.getOfferings();
const packages = offerings.current?.availablePackages ?? [];
const rows = packages.map((pkg) => ({
packageId: pkg.identifier, // e.g. "$rc_monthly"
productId: pkg.product.identifier, // store product id
title: pkg.product.title, // localized product title
price: pkg.product.priceString, // localized price, e.g. "$9.99"
}));
console.log(rows);
The priceString (called localizedPriceString on iOS and
price.formatted on Android) is provided by the store and is already formatted for the
user's region. Render it as-is.
Get Raw Products by ID
When you need a specific product by its store identifier (for example a one-off consumable that is not
part of an offering), call getProducts. It returns an array of StoreProduct
objects.
// React Native: fetch products by id
import Purchases, { PRODUCT_CATEGORY } from 'react-native-purchases';
const products = await Purchases.getProducts(
['my_product_id'],
PRODUCT_CATEGORY.SUBSCRIPTION,
);
if (products.length > 0) {
const product = products[0];
console.log(product.identifier, product.priceString);
}
// iOS (Swift): fetch products by id
let products = await Purchases.shared.products(["my_product_id"])
if let product = products.first {
print(product.productIdentifier, product.localizedPriceString)
}
// Flutter (Dart): fetch products by id
List<StoreProduct> products = await Purchases.getProducts(['my_product_id']);
if (products.isNotEmpty) {
final product = products.first;
print('${product.identifier} ${product.priceString}');
}
StoreProduct Fields Reference
A StoreProduct exposes the same core information on every platform, though a few field
names differ slightly. These are the fields you will use most:
| What you want | Field | Notes |
|---|---|---|
| Product id | identifier / productIdentifier |
The store product id. iOS uses productIdentifier. |
| Display name | title |
Localized product title from the store. |
| Description | description |
Localized product description from the store. |
| Raw price | price |
A number. Useful for comparisons, not for display. |
| Localized price string | priceString / localizedPriceString |
Already formatted, e.g. "$9.99". Display this. iOS uses localizedPriceString. |
| Currency | currencyCode |
ISO currency code, e.g. "USD". Already baked into the price string. |
On Android, the localized price is read through the price object as product.price.formatted.
If you are unsure of an exact field name on a given platform, consult the
official RevenueCat docs.
Always Use the Localized Price String
The single most important rule when displaying prices: always show the store-provided localized
price string (priceString / localizedPriceString / price.formatted).
Never hardcode a currency symbol or amount, and never build the price from the raw price
number yourself.
- The localized string already includes the correct currency symbol and locale formatting for the user.
- Prices differ by region, and stores apply rounding rules you should not try to reproduce.
- Hardcoding amounts breaks the moment you change pricing or launch in a new market.
pkg.product.priceString (or the platform equivalent) directly
in your paywall. Do not. Write something like "$" + product.price, which
ignores currency, locale, and store rounding.
Prefer Offerings (configured in the dashboard) so you can change which products you sell without an app
update. Use getProducts only when you need a specific product by id.
FAQ
How do I get products with purchases_flutter (getProducts)?
Call await Purchases.getProducts(['my_product_id']), which returns a
List<StoreProduct>. Each item has identifier, title,
description, price (a number), priceString (localized), and
currencyCode. For paywalls, prefer Purchases.getOfferings() and read
offerings.current?.availablePackages.
How do I get the localized price from a StoreProduct?
Read the store-formatted string: product.priceString on React Native and Flutter,
storeProduct.localizedPriceString on iOS, and product.price.formatted on
Android. These already include the user's currency and locale formatting.
What is the difference between getOfferings and getProducts?
getOfferings returns the packages you configured in the dashboard, so you can change what
you sell without an app update. getProducts fetches specific products by store id. Prefer
Offerings for paywalls.
Which StoreProduct fields are available?
The product id (identifier / productIdentifier), title,
description, the raw price number, the localized price string
(priceString / localizedPriceString), and currencyCode. Always
display the localized price string.
Related Guides
- Configure the RevenueCat SDK: apiKey and appUserID setup before fetching products
- Check Intro Offer Eligibility: show the right trial or intro price per user
- Get CustomerInfo & Refresh the Cache: read entitlements after a purchase
- React Native In-App Purchases Tutorial: full end-to-end integration
- iOS In-App Purchases Tutorial: full end-to-end integration
- Android In-App Purchases Tutorial: full end-to-end integration
- Flutter In-App Purchases Tutorial: full end-to-end integration
- RevenueCat Docs: official reference