Skip to main content
RapidDev - Software Development Agency
lovable-issues

How to Connect External API Endpoints (Stripe, OpenAI) to Lovable

To connect external APIs (Stripe, OpenAI, weather services, etc.) to a Lovable project, create a Supabase Edge Function that acts as a server-side proxy. Your React frontend calls the Edge Function, which adds the API key from Secrets and forwards the request to the external service. This avoids CORS errors and keeps API keys secure. Store all API keys in Cloud tab → Secrets — never in frontend code.

Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Advanced10 min read~15 minAll Lovable versionsMarch 2026RapidDev Engineering Team
TL;DR

To connect external APIs (Stripe, OpenAI, weather services, etc.) to a Lovable project, create a Supabase Edge Function that acts as a server-side proxy. Your React frontend calls the Edge Function, which adds the API key from Secrets and forwards the request to the external service. This avoids CORS errors and keeps API keys secure. Store all API keys in Cloud tab → Secrets — never in frontend code.

Why external API calls from Lovable need a server-side proxy

When you want your Lovable app to call an external API like OpenAI, Stripe, or a weather service, you cannot make the request directly from your React frontend code. Two problems prevent this. First, CORS (Cross-Origin Resource Sharing) blocks browser requests to different domains unless the API explicitly allows your domain. Most third-party APIs do not add your Lovable domain to their CORS headers because they expect server-to-server communication, not browser requests. Second, security. External APIs require secret keys for authentication. If you put an API key in your React code, it is visible to anyone who opens the browser developer tools. Even environment variables with the VITE_ prefix are embedded in the built JavaScript — they are not truly secret. The solution is a server-side proxy using Supabase Edge Functions. Your frontend sends a request to your own Edge Function (same origin, no CORS). The Edge Function reads the API key from Secrets (truly secure, never in browser code) and makes the request to the external API. The Edge Function then returns the response to your frontend.

  • Calling external APIs directly from React components — blocked by CORS
  • API keys in frontend code — visible in browser developer tools
  • VITE_ prefixed API keys — still embedded in built JavaScript, not truly secret
  • No error handling for API failures — app crashes when the external service is down
  • Rate limiting not handled — too many requests from the frontend exhaust API quotas

Error messages you might see

Access to fetch at 'https://api.openai.com/v1/chat/completions' from origin 'https://your-app.lovable.app' has been blocked by CORS policy

You are calling the OpenAI API directly from the browser. OpenAI does not allow browser-origin requests. Create a Supabase Edge Function proxy to make this call server-side.

401 Unauthorized: Incorrect API key provided

The API key you are sending is invalid, expired, or missing. Check Cloud tab → Secrets and verify the key matches what the API provider dashboard shows. Make sure the Edge Function reads the correct secret name.

429 Too Many Requests

You have exceeded the external API's rate limit. Add retry logic with exponential backoff in your Edge Function, and consider caching responses to reduce the number of API calls.

TypeError: Failed to fetch

This generic browser error usually means the request was blocked by CORS. Check the Network tab in developer tools for the actual response — the browser hides CORS error details in the generic TypeError.

Before you start

  • A Lovable project open in the editor
  • An API key or token from the external service (OpenAI, Stripe, etc.)
  • The external API's documentation (endpoint URLs, request format, authentication method)
  • Access to Cloud tab → Secrets and Cloud tab → Edge Functions

How to fix it

1

Store the external API key in Cloud tab Secrets

API keys must be stored securely on the server — never in frontend code or VITE_ variables

Click the + button next to Preview to open the Cloud tab. Go to Secrets and click Add Secret. Enter the secret name (for example OPENAI_API_KEY or STRIPE_SECRET_KEY) and paste the API key value. Do not use the VITE_ prefix for secret API keys — they should only be accessible from Edge Functions, not from the browser. The secret is encrypted and automatically available to all Edge Functions via Deno.env.get().

Before
typescript
// WRONG: API key in frontend code — exposed to everyone
const response = await fetch('https://api.openai.com/v1/chat/completions', {
headers: { 'Authorization': 'Bearer sk-abc123secretkey' },
});
After
typescript
// CORRECT: API key stored in Secrets, accessed only in Edge Functions
// Cloud tab → Secrets → Add: OPENAI_API_KEY = sk-abc123secretkey
// Edge Functions access it with: Deno.env.get('OPENAI_API_KEY')

Expected result: The API key appears in the Secrets list. It is encrypted and only accessible from Edge Functions.

2

Create an Edge Function proxy for the external API

The Edge Function makes the API call server-side, avoiding CORS and keeping keys secure

Prompt Lovable in Agent Mode to create an Edge Function. Be specific about which external API you are calling and what format the request and response should have. The Edge Function should: handle OPTIONS preflight requests with CORS headers, read the API key from Secrets, validate the incoming request, call the external API, and return the response. Lovable's AI will create the function file at supabase/functions/[function-name]/index.ts.

typescript
1// supabase/functions/openai-chat/index.ts
2import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';
3
4const corsHeaders = {
5 'Access-Control-Allow-Origin': '*',
6 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
7};
8
9serve(async (req) => {
10 if (req.method === 'OPTIONS') {
11 return new Response('ok', { headers: corsHeaders });
12 }
13
14 try {
15 const { messages } = await req.json();
16 const apiKey = Deno.env.get('OPENAI_API_KEY');
17
18 if (!apiKey) {
19 throw new Error('OpenAI API key not configured in Secrets');
20 }
21
22 const response = await fetch('https://api.openai.com/v1/chat/completions', {
23 method: 'POST',
24 headers: {
25 'Authorization': `Bearer ${apiKey}`,
26 'Content-Type': 'application/json',
27 },
28 body: JSON.stringify({
29 model: 'gpt-4',
30 messages,
31 max_tokens: 1000,
32 }),
33 });
34
35 const data = await response.json();
36
37 if (!response.ok) {
38 throw new Error(data.error?.message || 'OpenAI API error');
39 }
40
41 return new Response(JSON.stringify(data), {
42 headers: { ...corsHeaders, 'Content-Type': 'application/json' },
43 });
44 } catch (error) {
45 return new Response(JSON.stringify({ error: error.message }), {
46 status: 500,
47 headers: { ...corsHeaders, 'Content-Type': 'application/json' },
48 });
49 }
50});

Expected result: The Edge Function is created and deployed. You can call it from your frontend at /functions/v1/openai-chat.

3

Call the Edge Function from your React frontend

Your frontend talks to your own Edge Function (same origin) instead of the external API (cross-origin)

In your React component or service file, replace the direct external API call with a call to your Edge Function. Use the Supabase client's functions.invoke method or a plain fetch to /functions/v1/[function-name]. Pass only the data the Edge Function needs — the API key is already stored in Secrets and does not need to be sent from the frontend.

Before
typescript
// Direct external API call — blocked by CORS
async function askAI(question: string) {
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${import.meta.env.VITE_OPENAI_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ model: 'gpt-4', messages: [{ role: 'user', content: question }] }),
});
return response.json();
}
After
typescript
// Call through Edge Function proxy — no CORS, key is secure
import { supabase } from '@/integrations/supabase/client';
async function askAI(question: string) {
const { data, error } = await supabase.functions.invoke('openai-chat', {
body: { messages: [{ role: 'user', content: question }] },
});
if (error) throw new Error(error.message);
return data;
}

Expected result: The API call succeeds without CORS errors. The API key is never exposed to the browser.

4

Add error handling and loading states to your API integration

External APIs can fail, be slow, or rate-limit you — your UI must handle all these cases gracefully

Wrap your API calls in proper error handling with loading states, error messages, and retry logic. Users should see a loading indicator while the request is in progress, a helpful error message if it fails, and the result when it succeeds. For rate-limited APIs, add exponential backoff in the Edge Function so temporary failures retry automatically. If integrating multiple external APIs with different error patterns requires changes across many components, RapidDev's engineers have connected 600+ Lovable projects to external APIs.

Before
typescript
// No error handling — UI shows nothing on failure
function ChatComponent() {
const [answer, setAnswer] = useState('');
async function handleAsk() {
const data = await askAI(question);
setAnswer(data.choices[0].message.content);
}
}
After
typescript
// Proper loading, error, and success states
function ChatComponent() {
const [answer, setAnswer] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
async function handleAsk() {
setIsLoading(true);
setError(null);
try {
const data = await askAI(question);
setAnswer(data.choices[0].message.content);
} catch (err) {
setError(err instanceof Error ? err.message : 'Something went wrong');
} finally {
setIsLoading(false);
}
}
return (
<div>
<Button onClick={handleAsk} disabled={isLoading}>
{isLoading ? 'Thinking...' : 'Ask AI'}
</Button>
{error && <p className="text-red-500">{error}</p>}
{answer && <p>{answer}</p>}
</div>
);
}

Expected result: Users see a loading state during the request, a clear error message on failure, and the result on success.

Complete code example

src/hooks/useExternalApi.ts
1import { useState, useCallback } from 'react';
2import { supabase } from '@/integrations/supabase/client';
3
4/**
5 * Custom hook for calling external APIs through Edge Function proxies.
6 * Handles loading, error, and success states automatically.
7 *
8 * Usage:
9 * const { data, isLoading, error, execute } = useExternalApi<ResponseType>();
10 * await execute('function-name', { key: 'value' });
11 */
12export function useExternalApi<T = unknown>() {
13 const [data, setData] = useState<T | null>(null);
14 const [isLoading, setIsLoading] = useState(false);
15 const [error, setError] = useState<string | null>(null);
16
17 const execute = useCallback(
18 async (functionName: string, body: Record<string, unknown>) => {
19 setIsLoading(true);
20 setError(null);
21
22 try {
23 const { data: responseData, error: invokeError } =
24 await supabase.functions.invoke(functionName, { body });
25
26 if (invokeError) {
27 throw new Error(invokeError.message);
28 }
29
30 setData(responseData as T);
31 return responseData as T;
32 } catch (err) {
33 const message =
34 err instanceof Error ? err.message : 'API request failed';
35 setError(message);
36 throw err;
37 } finally {
38 setIsLoading(false);
39 }
40 },
41 []
42 );
43
44 const reset = useCallback(() => {
45 setData(null);
46 setError(null);
47 setIsLoading(false);
48 }, []);
49
50 return { data, isLoading, error, execute, reset };
51}

Best practices to prevent this

  • Always use Edge Function proxies for external API calls — never call external APIs directly from React components
  • Store API keys in Cloud tab → Secrets without the VITE_ prefix — they should only be accessible from Edge Functions
  • Create a custom React hook (useExternalApi) that handles loading, error, and success states consistently
  • Add proper error messages in Edge Functions — return descriptive errors so the frontend can show helpful messages
  • Implement retry logic with exponential backoff in Edge Functions for rate-limited or occasionally failing APIs
  • Validate incoming request data in Edge Functions before forwarding to the external API — reject malformed requests early
  • Log API call results in Cloud tab → Logs to monitor usage and debug failures

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I want to connect a Lovable (lovable.dev) project to an external API. My project uses Vite + React + TypeScript + Supabase. The external API I want to use: - Service: [OpenAI / Stripe / weather API / etc.] - API documentation link: [URL] - Authentication: [API key / OAuth / etc.] - Endpoints I need: [list the endpoints and what they do] Please: 1. Create a Supabase Edge Function proxy for this API 2. Show me how to store the API key securely 3. Create a React hook for calling the Edge Function with loading/error states 4. Add proper error handling and validation in the Edge Function 5. Show me how to handle rate limiting

Lovable Prompt

Create a Supabase Edge Function at supabase/functions/[API-NAME]-proxy/index.ts that proxies requests to [EXTERNAL API URL]. The Edge Function should: handle OPTIONS preflight with CORS headers, read the API key from Secrets using Deno.env.get('[API_KEY_NAME]'), validate the request body, call the external API endpoint, and return the response. Also create a React hook at src/hooks/use[ApiName].ts that calls this Edge Function using supabase.functions.invoke with loading, error, and data states. Store the API key in Cloud tab → Secrets as [API_KEY_NAME].

Frequently asked questions

How do I call an external API from Lovable?

Create a Supabase Edge Function that acts as a proxy. Your React frontend calls the Edge Function, which adds the API key from Secrets and forwards the request to the external API. This avoids CORS errors and keeps your API key secure.

Can I call the OpenAI API directly from a Lovable app?

Not directly from the browser — OpenAI blocks browser-origin requests (CORS). Create an Edge Function proxy that receives your request, adds the OpenAI API key from Secrets, and calls the OpenAI API server-side. Your frontend calls the Edge Function instead.

Where do I store API keys for external services in Lovable?

Store them in Cloud tab → Secrets without the VITE_ prefix. Keys without VITE_ are only accessible from Edge Functions (server-side), not from browser code. This keeps them truly secure. Access them in Edge Functions with Deno.env.get('API_KEY_NAME').

How do I handle errors when calling external APIs in Lovable?

Add try/catch blocks in both the Edge Function and the React component. The Edge Function should return descriptive error messages with appropriate HTTP status codes. The React component should show loading indicators, error messages, and handle retries for transient failures.

Can I connect to multiple external APIs in one Lovable project?

Yes. Create a separate Edge Function for each external API, or create a generic proxy function that accepts the target API as a parameter. Store each API's key as a separate secret in Cloud tab → Secrets. Use a custom React hook to call different Edge Functions with consistent loading/error handling.

Why do I get 'Failed to fetch' when calling an external API?

This usually means CORS is blocking the request. The browser prevents your Lovable frontend from calling a different domain directly. Create an Edge Function proxy so the API call happens server-side where CORS does not apply.

What if I need help integrating multiple external APIs?

Complex integrations with multiple APIs (Stripe payments, OpenAI chat, email services) require multiple Edge Functions, proper error handling, and coordinated secret management. RapidDev's engineers have connected 600+ Lovable projects to external APIs and can set up the complete integration architecture.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your issue.

Book a free consultation

Need help with your Lovable project?

Our experts have built 600+ apps and can solve your issue fast. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.