Claude API React Native Integration Guide
Can you call the Claude API directly from a React Native app?
Technically yes, but you shouldn't. React Native apps ship as compiled bundles that users can decompile, inspect, and extract secrets from. If you embed your Anthropic API key in client code, it will eventually leak, and once it leaks, anyone can run unlimited requests on your bill. The correct architecture is: your React Native app talks to a thin backend layer, and that backend layer holds the real API key and forwards requests to Claude.
This article walks through that architecture end to end — how to structure the request flow, how to handle streaming responses on mobile, how to add tool use, and how a service like SubToAPI can replace the backend layer entirely if you don't want to build and maintain one yourself.
Why you need a backend layer at all
Anthropic's API expects requests to come from a trusted server, not an untrusted client. The same rule applies to any model provider. Putting your API key in app.json, an .env bundled into the app, or hardcoded in a fetch call means it's effectively public.
The standard pattern looks like this:
React Native app → Your backend / proxy → Claude API
Your backend is responsible for:
- Holding the API key securely (environment variable, secret manager)
- Authenticating your own app's users (so strangers can't hit your proxy)
- Forwarding the request to Claude and streaming the response back
- Optionally logging usage per user for billing or rate limiting
This is true whether you call Anthropic's API directly or through an API layer like SubToAPI — the key never belongs on the device.
Setting up the proxy with SubToAPI
If you don't want to run and secure your own proxy server, SubToAPI gives you an HTTPS endpoint that issues application-scoped keys (sub_live_...) instead of raw provider credentials. You still need a thin backend (or a serverless function) in front of your React Native app, but you no longer manage model provider auth, streaming plumbing, or usage metadata yourself — that's handled for you.
A minimal Node/Express proxy that your React Native app calls:
// server.js
app.post('/chat', async (req, res) => {
const response = await fetch('https://api.subtoapi.app/v1/messages', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SUBTOAPI_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'claude-sonnet-4',
max_tokens: 1024,
messages: req.body.messages,
}),
});
const data = await response.json();
res.json(data);
});
Your React Native code only ever talks to /chat on your own server:
// ChatScreen.js
async function sendMessage(text) {
const response = await fetch('https://yourapp.com/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
messages: [{ role: 'user', content: text }],
}),
});
const data = await response.json();
return data.content[0].text;
}
The SUBTOAPI_KEY lives only on your server, never in the mobile bundle. See the full request/response shape in the docs and the quickstart if you're starting fresh.
Streaming responses into a React Native chat UI
A static "wait for the whole answer" experience feels slow on mobile. Streaming tokens as they arrive makes the app feel responsive, but React Native's fetch doesn't expose a readable stream the same way browsers do, and most RN environments don't support Server-Sent Events natively.
Two practical approaches:
1. Use react-native-sse or a streaming-capable fetch polyfill on the client, pointed at your backend's streaming endpoint, which itself proxies Claude's stream from SubToAPI (see streaming docs).
2. Use a WebSocket bridge. Your backend opens a stream to Claude, and relays chunks to the app over a WebSocket connection — this avoids a lot of the inconsistent HTTP streaming support across RN's JS engines (Hermes vs JSC).
Backend streaming proxy sketch:
app.post('/chat-stream', async (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
const upstream = await fetch('https://api.subtoapi.app/v1/messages', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SUBTOAPI_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'claude-sonnet-4',
max_tokens: 1024,
stream: true,
messages: req.body.messages,
}),
});
for await (const chunk of upstream.body) {
res.write(chunk);
}
res.end();
});
On the client, buffer incoming chunks and append text deltas to your message bubble as they arrive. This gives you the token-by-token feel users expect from a Claude-powered chat app without needing to implement SSE parsing from scratch on-device.
Adding tool use for app-specific actions
If your app needs Claude to trigger actions — look up order status, fetch a user's calendar, call an internal API — define tools on your backend request, not in the app. The mobile client just displays results; your server decides what tools are available and executes them when Claude requests a call.
body: JSON.stringify({
model: 'claude-sonnet-4',
max_tokens: 1024,
messages: req.body.messages,
tools: [
{
name: 'get_order_status',
description: 'Look up the status of an order by ID',
input_schema: {
type: 'object',
properties: { order_id: { type: 'string' } },
required: ['order_id'],
},
},
],
})
Full tool-calling mechanics, including how to return tool results back to Claude in the next turn, are in the tools documentation.
Handling usage and rate limits across many app users
A single React Native app might have thousands of users hitting your backend simultaneously. If you're managing raw Anthropic keys, you're responsible for rate limiting, retry logic, and per-user usage tracking yourself. SubToAPI's dashboard gives you usage metadata per API key out of the box, which is useful if you issue separate sub_live_... keys per environment (dev, staging, production) or per team, and want visibility without building your own logging pipeline. Plans start at Solo €9/month, with Team (€19/seat) and Scale (€49/seat) tiers for larger apps — see pricing — and a free trial at signup if you want to test the proxy pattern before committing.
Summary
Integrating the Claude API into React Native comes down to one rule: never put the key on the device. Build a small backend proxy, stream responses through it, define tools server-side, and let a service handle provider auth and usage tracking if you don't want to build that layer yourself.
FAQ
Can I call Claude directly from React Native without a backend? Not safely. Any API key bundled into the app is extractable. Always route requests through a server you control.
How do I stream Claude's responses into a chat UI on mobile? Proxy the stream through your backend and relay chunks to the app via SSE polyfill or WebSocket, since RN's native fetch doesn't fully support readable streams.
Does SubToAPI replace my backend entirely? No — you still need a thin server or serverless function to authenticate your app's users and hold the API key. SubToAPI replaces the provider-auth and usage-tracking layer behind that server.