Appearance
Working with APIs & External Services
Your web app is running. It looks good. It does what you built it to do. But it works in a bubble. It does not talk to anything outside itself: no weather data, no payment processing, no email notifications, no external data sources. Real applications connect to real services, and vibe coding handles API integration the same way it handles everything else: you describe the integration, and the agent writes the code, handles the authentication, manages the error cases, and parses the responses.
This lesson builds an API integration from one prompt. The example connects to a weather API, but the pattern is identical for any external service: a payment processor, a mapping service, a data feed, a notification system. The skill is the same. Only the endpoint and the data shape change.
What you'll learn
- API integration through vibe coding means describing the service, the authentication method, and the response format you want
- The agent handles the mechanical parts: HTTP client setup, auth headers, error handling, response parsing, rate limit awareness
- The real skill is knowing what to describe: which endpoints, which auth method, which fields from the response you actually need
- Test the integration immediately after the agent builds it. External services fail for reasons the agent cannot predict
The problem: external services have their own rules
Every API is different. Different authentication schemes. Different response formats. Different error codes. Different rate limits. In a traditional workflow, you read the API documentation, figure out the authentication flow, write the HTTP calls, handle the responses, deal with the errors, and write tests against a mock server. For a weather API, that is maybe an hour of work. For a payment processor with webhooks, idempotency keys, and PCI compliance, it is a week.
Vibe coding collapses that hour or that week into a prompt. The agent reads the API documentation (you point it at the docs with an @ reference), writes the integration code, and handles the error cases. Your job is describing what you want and verifying the output works against the real service.
Options & when to use each
| Integration pattern | What it is good for | What it costs you | When to pick it |
|---|---|---|---|
| Direct HTTP (fetch/axios/requests) | Full control, no abstraction overhead, agent produces the exact code you need | More lines of code; you or the agent must handle retries, timeouts, and error parsing manually | Simple APIs with 1-3 endpoints; when you want minimal dependencies |
| Official SDK | Auth handled automatically, response types included, fewer lines of code | SDK may be poorly maintained or not match the API's current behavior; adds a dependency | Production integrations with major services (Stripe, AWS, Twilio) |
| Generated client (OpenAPI) | Types generated from spec, zero hand-written HTTP code | Depends on a current OpenAPI spec; regeneration needed when the API changes | APIs that publish an OpenAPI spec; when you are integrating with many endpoints |
| Serverless function proxy | Hides API keys from the client, adds caching and rate limiting | Additional infrastructure; more moving parts to debug | Public-facing apps where API keys must not be exposed to the browser |
Build it: a weather dashboard from one prompt
Step 1: Describe the integration
You have the project dashboard from the previous lesson. You want to add weather data to it. Here is the prompt:
> Add weather data to the project dashboard. Use the OpenWeatherMap API.
You need a free API key from https://openweathermap.org/api. Store it in a
.env file as VITE_WEATHER_API_KEY (the VITE_ prefix exposes it to the
frontend through Vite's env handling).
Read the current weather API docs at:
@https://openweathermap.org/current
Build a WeatherWidget component that:
- Shows current weather for a configurable city (default: New York)
- Displays: city name, temperature (Fahrenheit), conditions (icon + text),
humidity, wind speed
- Fetches data when the component mounts
- Shows a loading spinner while fetching
- Shows an error message if the API call fails (rate limited, city not
found, network error)
- Refreshes data every 10 minutes
Add the widget to the dashboard sidebar. Handle all error cases explicitly:
network errors, invalid API key, city not found, rate limiting (429).
Log errors with console.error so they show up in the browser dev tools.
Use the fetch API, no external HTTP library needed.Step 2: The agent's output
The agent produces:
.env entry:
VITE_WEATHER_API_KEY=your_key_here/src/components/WeatherWidget.tsx: A React component that:
- Reads the API key from
import.meta.env.VITE_WEATHER_API_KEY - Constructs the OpenWeatherMap URL with the city and API key
- Fetches data on mount with
useEffect - Manages three states:
loading,error,data - Renders a spinner during loading
- Renders an error message with a retry button on failure
- Renders the weather display on success
- Refreshes every 10 minutes with
setInterval
The agent handles the mechanical work you would have done manually: constructing the URL with query parameters, parsing the JSON response, mapping the weather condition codes to display text, converting Kelvin to Fahrenheit, and formatting the output.
Step 3: Verify against the real API
Register for a free API key at openweathermap.org. Add it to your .env file. Run npm run dev and open the dashboard. The weather widget should load and display current weather for New York.
Test each error case:
- Remove the API key from
.envand reload. The widget should show an error message about authentication. - Change the city to "XyzzyNotARealCity" and reload. The widget should show "City not found."
- Disconnect your network and reload. The widget should show a network error message with a retry button.
If any error case produces a blank screen or a console crash instead of a handled error, tell the agent: "The WeatherWidget crashes when the API returns a 404. Add proper error handling for that case." The agent fixes it.
Step 4: Add a second API (optional extension)
Now add a second external service. A quote of the day, a stock ticker, a news headline. The pattern is identical:
> Add a random programming quote to the dashboard footer. Use the free
Programming Quotes API at https://programming-quotesapi.vercel.app/api/random.
Build a QuoteWidget component that:
- Fetches a random quote on page load
- Shows the quote text and author
- Shows a "New quote" button that fetches another
- Handles loading and error states the same way as WeatherWidgetThe agent reuses the patterns it learned from the weather integration: the same loading/error/data state management, the same component structure, the same error handling. This is the compounding effect: each integration makes the next one faster because the agent has reference code to follow.
What goes wrong
| Mistake | How you notice it | The fix |
|---|---|---|
| API key exposed in client-side code | Your API key appears in the browser's Network tab or in the built JavaScript bundle | Use environment variables with the framework's mechanism (VITE_ prefix for Vite). For truly secret keys, proxy through a backend endpoint |
| Not specifying error handling explicitly | The agent wraps the fetch in a try/catch that catches everything and shows "Something went wrong," which tells the user nothing useful | Describe error cases by HTTP status code: "On 401, show 'Invalid API key.' On 404, show 'City not found.' On 429, show 'Too many requests, try again in a minute.'" |
| Using the API's documented format without testing | The docs say the response has a main.temp field. The API actually returns main.temp nested differently in some edge cases. The widget crashes on those cases | Test with the real API response. Console.log the response before parsing it. Ask the agent to add defensive checks: "Verify the response shape before accessing nested fields." |
| Not setting a timeout | The API is slow or unresponsive. The widget shows a spinner forever. The user assumes the app is broken | Add a timeout to every fetch: "Fetch with a 10-second timeout. On timeout, show a 'Service is slow, try again' message." |
| Ignoring rate limits | The widget works during development. In production, 100 users load the page simultaneously, and the API returns 429 for all of them | Add caching: "Cache the weather response for 10 minutes in localStorage. Only fetch if the cache is expired." Or proxy through a backend that shares one API call across all users |
Confirm it worked
Open the browser dev tools (F12), go to the Network tab, and reload the dashboard. You should see the API request to OpenWeatherMap. Click on it and inspect the response. Confirm the widget displays the correct temperature, conditions, and city name from the response.
Then test the failure modes:
- Stop your local server. Reload. The widget should show a network error, not a blank screen.
- Use a fake API key. Reload. The widget should show an authentication error.
- Set the refresh interval to 5 seconds temporarily. Confirm it updates on schedule.
- Open the Console tab. Confirm there are no unhandled promise rejections.
The integration is complete when: the widget works with a valid API key, fails gracefully in every error case you can trigger, and does not produce console errors or unhandled rejections.
Next: Database-Backed Applications -- add real persistence to your vibe-coded app with SQLite or Postgres.