Bootcamp
Connect a real API with private-key auth: the Bitly walkthrough
Photo by engin akyurt on Unsplash
Sooner or later every serious app needs to talk to something outside itself: a payment platform, an email tool, an accounting system. In Bubble, that conversation happens through the API Connector, and the first time you configure it is the moment no-code starts to feel like real software engineering. Here’s the complete path: the concepts, the documentation, the auth options, then a working Bitly integration from first click to a shortened link on the page, with the error handling and debugging that separate a demo from something you’d ship.
An API (application programming interface) lets two applications communicate: one sends a request, the other returns a response. That exchange, one request and one response, is a single API call.
Requests, responses and webhooks
Think of the weather app on your phone: it measures nothing itself, it sends an API request to the weather bureau’s system, which returns the day’s data as a response. Your app asks, the other system answers.
A webhook is the mirror image: an automated, one-way message sent to your app when something happens elsewhere, the way a SaaS builder might ask Stripe to notify their app when a user’s subscription payment is declined so it can disable that user’s permissions. An API call is a two-way request and response you initiate; a webhook is a one-way transmission triggered by an event on the other side.
Start with the documentation
API documentation is the single most important artefact in any integration. It tells you three things: which tasks the API can accomplish (can it create customers? list invoices?), which authentication method it requires, and what sample requests and responses look like. Google’s Time Zone API docs show the typical shape: an introduction, a section on authentication, then the endpoints. Every integration starts by finding and reading the reference.
The three authentication methods you’ll meet
Authenticating means connecting securely; instead of a username and password typed into a form, APIs usually want a token or key.
- Private key authentication. The provider issues a unique key for each developer or app, and you pass it in the header of every request. The most common pattern, and the one we’ll build below.
- Basic authentication. A username and password sent, encoded, in the header of each call. Simpler, and you still see it occasionally.
- OAuth2. The “log in with Google” pattern: users authorise your app without ever giving it their password, and your app receives a token limited by scope. Very secure and increasingly common, but noticeably more complex to implement.
Inside the API Connector
The API Connector is a plugin, available on Bubble’s Starter plan and above. Install it from the Plugins tab.
Press Add another API and work top to bottom.
- API name identifies this connection across your app; the calls beneath it belong to one provider (for Stripe, perhaps Create Customer and Get Customers).
- Authentication is a dropdown of methods; choose according to the documentation. One or two options cover most APIs.
- Shared headers are key-value pairs sent with every call in this connection. The values are encrypted, so this is a safe home for API keys. Anything common to all calls belongs here, not repeated per call: don’t repeat yourself.
- Shared parameters work the same way but are appended to the end of the URL, giving the server extra information about what’s requested.
Each call has its own settings:
- Name it as an action that describes the functionality: “create customer”, “shorten a link”.
- Use as confuses everyone at first. Data makes the call available as a data source (“Get data from an external API”); Action makes it a workflow step. Choose deliberately.
- Data type is what the API returns; JSON, a structure of key-value pairs and lists, is by far the most common.
- Method: GET asks for information without changing anything; POST asks the server to create (sometimes update) something; PUT replaces; PATCH edits an existing thing; DELETE deletes.
- URL is the endpoint, copied from the documentation.
- Attempt to make the call from the browser is only for APIs needing no authentication (country codes, time zones, public weather data). Anything involving keys or tokens must run from the server, so credentials never reach the browser.
- Per-call headers and parameters apply to this call only, on top of whatever is shared.
Finally, every call must be initialised: Bubble makes the request once and captures the structure of the response; only then can your app use it.
Step by step: connect Bitly with a private key
Bitly turns long URLs into short ones you can drop into a text message or print on a poster: a perfect first integration.
- Create a Bitly account. Sign up with a username and password if you can; you’ll need the password shortly. If you signed up with Google, create a password in your profile settings first.
- Check your Bubble plan. In Settings, make sure the app is on the Starter or Agency plan, then install the API Connector if you haven’t.
- Find the API reference. Bitly authenticates with an access token passed in an authorization header as
Bearerplus your token. - Generate your token. Tokens usually hide in a platform’s settings under something like developer settings, integrations or API. In Bitly it’s the API section: enter your password, generate, copy.
- Create the API. In the API Connector, add another API and name it
bitly. Leave authentication self-handled. Add a shared header: keyAuthorization, valueBearerthen a space then your pasted token. Add a second shared header,Content-Type=application/json, because the docs ask for it. - Create the call. Name it
shorten. The docs show “shorten a link” is a POST, so set the method to POST and paste the endpoint URL from the reference. Set Use as to Action (we want it in a workflow) and the type to JSON. - Add the body. Copy the sample JSON object from the docs (copying beats retyping; hand-typed JSON often breaks). Keep
long_url, the link to shorten. Delete the optionaldomainandgroup_guidfields: domain defaults to Bitly’s own, and the group ID matters only for accounts with custom setups. - Initialize the call. When you see return values, it worked; otherwise you’ll usually get an error message. Show the raw data and skim what came back: creation date, ID, the short link, the long URL, tags. Set each field’s data type: the timestamp is a date (Bubble doesn’t always detect this), the rest are texts. Press save, and those fields become available throughout your app.
Wire it into a page
Now use the call. Build a small test page: an input long URL and a create button grouped together, and a text below reading “Your short URL is…” bound to a custom state short URL on the parent group.
- Add a workflow to the create button and search “bitly”. The
bitly - shortenaction appears only if the call is initialised and set to Action. Set it as Data by accident and it won’t show in the actions list (and vice versa). If you’re staring at an empty list, this is almost always why. - Rather than editing raw JSON inside the action, go back to the connector and make the body value dynamic: replace the hard-coded URL with a parameter named
long_url, and untick private so the app can supply the value at run time (private means it stays fixed). - Back in the workflow, set
long_urltoInput long URL's valueusing insert dynamic data. Bitly requires the value to start withhttps://(orhttp://); without it, the call errors. - Add a second step: set the group’s
short URLstate toResult of step 1's link. Any field you saved at initialisation is available here. - Preview: type a URL, hit create, and the shortened link appears on the page and resolves to your site.
That’s the full loop: authorization header, content type, an action that shortens the URL, and the result flowing back into your interface. A huge share of real-world APIs work exactly like this; get this example running and you can handle most integrations.
A better home for the key: private key in header
There’s a second way to configure the same connection. Create the API again (call it bitly2), but choose Private key in header as the authentication type: Authorization as the key name, Bearer plus your token as the development key value (don’t forget the word Bearer), the shared Content-Type header kept, the same call rebuilt.
Functionally identical, with one real advantage: this option lets you specify a different key for development versus live, and often a separate account with it, so you never create test data in your production account.
Parameters instead of a JSON body
A third variation: delete long_url from the JSON body and send it as a parameter. Same result, with practical differences worth knowing.
- Parameters can be marked optional, and an optional parameter you don’t supply simply isn’t sent. With a JSON body, an empty dynamic value can go across as null and overwrite data that already exists in the remote system.
- The querystring option sometimes succeeds where a body fails; Stripe is a known example.
- You sidestep JSON syntax altogether. Hand-built JSON is fragile: every key and value needs correct quoting, every pair but the last needs a comma, and lists and nested objects multiply the ways one missing character breaks the whole call.
Handle errors without the ugly popup
Type an invalid URL into the test page and Bitly rejects it: “The value provided is invalid”, and Bubble halts the workflow with a popup. Error handling replaces that dead end with a path.
- In the call’s settings, tick ignore errors in response and allow the workflow action to continue (there’s also an option to capture the response headers). Re-initialise the call.
- Four new fields appear on the result:
error status code,error status message,error bodyandreturned an error. That last yes/no flag is the useful one. - In the workflow, set the
short URLstate only whenResult of step 1's returned an error is no. - Add a text element with a friendly message: “Sorry, your URL could not be shortened. Please check it is a valid URL.” Set it hidden on page load and collapse when hidden.
- Add a custom state
error(yes/no), set it to yes when the call returned an error, and show the message conditionally on that state. On success, seterrorback to no and clear the old short URL, or a stale state can leave the error showing after a valid run.
Now a bad URL shows a calm message instead of a popup. That matters beyond cosmetics: if the next step were saving the short link to your database, you’d want to skip it when there’s no data to save. The error flag gives your workflow a second path of logic.
Debug when the logs tell you nothing
Trigger the error, then open Bubble’s server logs: the button click, the action running, the request, the response headers, and precisely nothing that explains what went wrong. That’s not unusual with APIs.
The fix is a reusable debug pattern: create a Debug data type, and in the workflow add a “Create a new thing” step that saves Result of step 1's raw body, the complete JSON the server returned, success or failure. Now every run leaves a record. Open App Data after a failed call and there’s Bitly’s actual response: invalid argument, field long_url, code invalid. Paste the JSON into a code-oriented text editor to see its structure clearly; one long row is hard to read.
Bitly’s errors are unusually helpful; many APIs return generic ones. Either way the comparison is stark: half an hour squinting at logs, or ten seconds reading the raw body. Building yourself this kind of scaffolding, easier ways to see what’s going on so you can fix problems fast, is the mark of a genuinely good developer.
Try it yourself
Rebuild the test page from scratch in your own app. Connect to Bitly with the bearer-token method, make a GET call that displays data in a repeating group (your list of bit links is ideal), then a POST call that creates a new short link. Then pick any other endpoint in the Bitly reference; the pattern barely changes.
This skill in the AI era
API integration is the skill that turns one app into an operating system for a business, and it has aged better than almost anything else in the course. The same moves, reading the docs, choosing the auth pattern, keeping keys out of the client, logging the raw response, are exactly what we apply when wiring AI agents into Gmail, Xero or a CRM in our AI automation work for small business. Claude Code will happily write the request code, but knowing what a bearer token is and why the error path needs its own logic is what makes the result trustworthy. That judgement is precisely what carried over from Bubble into AI-assisted development.
If this sparked something, let's talk.
No pitch, no pressure — just a conversation about what you're working on.
Let's talkRelated posts
Bootcamp
A simple approach to data modelling
A four-step method for designing a database schema: identify entities, map relationships, choose fields. Taught through the CRM data model built in Bubble.
Bootcamp
Module 5 — Application architecture: pages, states and reusable pieces
Single-page vs multi-page apps, URL parameters, custom states, reusable elements — and a big practical building a main template with an expanding navigation drawer.
Bootcamp
API calls as actions vs data (and the planning checklist)
When to use Bubble API calls as actions versus data sources, a one-to-many auth pattern, and the planning checklist that keeps integrations from failing silently.