How to Verify Emails in Bubble.io With the API Connector
To verify emails in Bubble.io, add your verification API to the API Connector with Private key in header, create a POST call set to Use as: Action, and tick Include errors in response and allow workflow actions to continue. In your sign-up workflow, run the verify call first, then Sign the user up only when the result's status is not invalid. Show the reason when it is. For users you already have, a backend workflow scheduled on a list with a 1-second interval verifies them under the API's rate limit.
This guide sets that up step by step with SimpleVerifier's API. The same steps work with any verification API that takes an email and returns a status.
What you will build
Three pieces:
- An API Connector call named Verify email, with the key stored privately.
- A sign-up workflow that checks the address before creating the user, and tells the person what is wrong when it fails.
- A backend workflow that verifies existing users in batches and saves the result on each User.
Verification sits between the form and the account. Our guide to real-time verification on signup forms explains the timing trade-offs. This guide covers Bubble.
Step 1: Add the API with a private key
Install the API Connector plugin from the Plugins tab if your app doesn't have it. Then Add another API and name it SimpleVerifier.
Set the authentication at the collection level. Bubble's API Connector authentication guide describes Private key in header with three fields:
| Field | Value |
|---|---|
| Authentication | Private key in header |
| Key name | Authorization |
| Private key | Bearer sv_live_... (your key, with "Bearer" and a space first) |
| Development key value | Optional. A second key for your development version |
Bubble's guide says that once configured, "the API Connector adds the header to every call in the collection automatically." It also says to include a prefix such as Bearer before the key, separated by a space.
On SimpleVerifier, keys start with sv_live_ and are shown once. The API docs explain how to create one. API access comes with a subscription. Pay-as-you-go packs cover CSV uploads only.
Why the key is safe here
Bubble's API Connector overview says calls are routed through Bubble's server by default, which keeps credentials off the user's device. Its API Connector security page adds the rules that matter:
- Keys should never be stored where they can show up in your app's source code. Bubble names option sets, workflows, custom states, URL parameters and data type field defaults.
- Initialization default values are visible in the app's code. Don't put sensitive values there, and clear them after initializing.
- If a key ends up somewhere it shouldn't, disable it and create a new one.
Calling a verification API from the browser would expose the key. Anyone who can read it can use your plan. The API Connector avoids that.
Step 2: Create the Verify email call
Add a call under the SimpleVerifier API. Bubble's guide to adding calls documents each setting:
| Setting | Value |
|---|---|
| Name | Verify email |
| Use as | Action |
| Data type | JSON |
| Method | POST |
| URL | https://simpleverifier.com/api/v1/verify |
| Body type | JSON |
| Body | { "email": "<email>" } |
| Include errors in response and allow workflow actions to continue | Ticked |
Typing <email> in the body creates a parameter called email. Leave its Private box unticked, because your workflows need to fill it with each address. The address is not a secret. The key is, and it already lives in the private header.
Action, not Data
Bubble's docs say a Data call populates elements and appears under "Get data from an external API". An Action call runs from a workflow. Use Action. You want exactly one call, at one step, when someone presses Sign up. Not one every time an element loads.
Why tick "Include errors in response"
Without it, an error from the API stops the workflow. With it, Bubble's docs say you get an error object with four fields: status code, status message, body and has returned error. The workflow carries on.
That turns an API problem into a non-event for your users. If the call fails, the status field is empty, and the sign-up condition in Step 4 still passes. Bubble's docs say to re-initialize the call if you change this option after initializing.
Step 3: Initialize the call
Bubble's docs say initialization sends a real request, then reads the response so you can use its fields in the editor. Enter an address you own as the test value for email and click Initialize call.
The response looks like this:
{
"email": "jane@company.com",
"status": "valid",
"reason": "Mailbox exists",
"mailbox_checked": true
}
Save it. Bubble now offers status, reason, email and mailbox_checked wherever you use the call's result. Then clear the test value, as Bubble's security page advises for default values.
What the fields mean
status |
Meaning | Possible reason values |
|---|---|---|
| valid | The mail server confirmed the mailbox exists | Mailbox exists |
| risky | It may work, but delivery isn't confirmed | Accepts all addresses, Role-based address, Provider hides mailbox status, Server would not confirm, Mailbox check unavailable |
| invalid | Don't send. It will bounce or it's a throwaway | Invalid format, No mail server, Disposable address, Mailbox not found |
Only invalid should stop a sign-up. risky usually means a catch-all domain, a shared inbox or a provider that hides mailbox status. Plenty of real customers sit behind those. Our guide to accept-all vs unknown results covers each reason.
Step 4: Check the address before Sign the user up
Add three fields to the User data type first, so you keep the result:
| Field | Type |
|---|---|
email_status |
text |
email_reason |
text |
email_checked_at |
date |
Add a custom state on the page, email_error (text), and a text element that shows it under the email input.
Now open the Sign up button's workflow and build these steps:
| Step | Action | Settings | Only when |
|---|---|---|---|
| 1 | Plugins > SimpleVerifier - Verify email | email = Input Email's value |
Always |
| 2 | Set state of the page | email_error = Result of step 1's reason |
Result of step 1's status is "invalid" |
| 3 | Terminate this workflow | Result of step 1's status is "invalid" | |
| 4 | Sign the user up | Email = Input Email's value, Password = Input Password's value | |
| 5 | Make changes to Current User | email_status = Result of step 1's status, email_reason = Result of step 1's reason, email_checked_at = Current date/time |
Step 3 stops the workflow on an invalid address, so Sign the user up never runs. Every other case, including an API error with an empty status, reaches Step 4.
Friendlier messages
The raw reason, such as "Mailbox not found", is clear enough for most apps. For softer wording, create an option set called Email reasons with an attribute message, and map each invalid reason:
| Reason | Message to show |
|---|---|
| Invalid format | That email address doesn't look right. Please check it. |
| No mail server | That email domain can't receive mail. Please check the spelling. |
| Disposable address | Please sign up with a permanent email address. |
| Mailbox not found | That mailbox doesn't exist. Please check the address. |
Option sets are fine for messages. They are only a problem for secrets.
Show that something is happening
A live mailbox check talks to the recipient's mail server. Most answer within a few seconds, and some are slower. Our API docs recommend allowing about 30 seconds. Disable the button and show a loading indicator while Step 1 runs, so nobody clicks twice.
Worked example: three sign-ups
Three people press Sign up. The outcomes are illustrative, not measured.
jane@gmial.com. Step 1 returnsinvalid, "No mail server". Step 2 shows "That email domain can't receive mail. Please check the spelling." Step 3 ends the workflow. Jane fixes the typo and signs up. Our guide to catching typos at signup shows how to suggest the fix directly.info@agency.example. Step 1 returnsrisky, "Role-based address". The account is created, and the User record shows the reason. Sales can decide whether to ask for a personal address later.sam@company.exampleduring an API outage. Step 1 returns an error object and an empty status. Steps 2 and 3 are skipped. Sam signs up normally with an emptyemail_status, which the backend workflow below will fill in.
Step 5: Verify existing users with a backend workflow
Sign-up checks protect new users only. For the ones you have, use a backend workflow. Bubble's docs say API workflows are edited in the Backend workflows section of the editor. If you can't see it, enable backend workflows in your app's API settings first.
The workflow that checks one user
Create an API workflow named verify-user with one parameter, user, of type User:
| Step | Action | Settings | Only when |
|---|---|---|---|
| 1 | Plugins > SimpleVerifier - Verify email | email = user's email |
Always |
| 2 | Make changes to a thing | Thing = user; email_status = Result of step 1's status; email_reason = Result of step 1's reason; email_checked_at = Current date/time |
Result of step 1's status is not empty |
The condition on Step 2 matters. A rate-limit or service error leaves the status empty, so the user stays unchecked and gets picked up on the next run.
Scheduling it on a list, under the rate limit
On an admin page, add a button whose workflow uses Schedule API workflow on a list:
| Setting | Value |
|---|---|
| Type of things | User |
| List to run on | Do a search for Users, constraint email_status is empty |
| API workflow | verify-user |
| Scheduled date | Current date/time |
| Interval | 1 |
| user | This User |
Bubble's scheduling guide explains the interval. It is the gap between scheduled workflows. Bubble says an interval can help when an external API has a strict rate limit. It also says the action supports up to 100,000 workflows, and that the scheduled workflows run in parallel rather than one after another.
SimpleVerifier allows 120 calls a minute per key, and returns HTTP 429 above that. Here is what each interval works out to:
| Interval (seconds) | Calls per minute (approx.) | Room left for live sign-ups |
|---|---|---|
| 0.5 | 120 | None |
| 1 | 60 | About 60 a minute |
| 2 | 30 | About 90 a minute |
Pick 1 second if your sign-up page shares the same key. The limit is per key, not per workflow. Our guide to handling API rate limits covers what 429 responses mean and how to back off.
Leaving the interval blank is not a good fit here. Bubble says a blank interval leaves only "a slight gap", and the guide warns that an interval of 0 can block other scheduled workflows until the whole batch is done.
How long it takes
At a 1-second interval, 3,000 users take about 50 minutes and 10,000 take close to three hours. Each scheduled workflow and API call also uses workload units on your Bubble plan. Check Bubble's pricing before running a large batch.
When to upload a CSV instead
Bubble backend workflows suit hundreds or a few thousand users. For a large user base, export the User table to CSV, upload it for bulk verification, and import the results back by unique ID or email. It avoids thousands of scheduled workflows and the workload units they use. Our guide on removing invalid emails from a CSV walks through the file side.
Common problems
| Symptom | Likely cause | Fix |
|---|---|---|
| Every call returns 401 | Private key missing "Bearer " | Re-enter it as Bearer sv_live_... |
| Every call returns 402 | Subscription not active | Check the plan; packs don't include API access |
| Workflow stops on an API error | Include errors in response not ticked | Tick it and re-initialize the call |
status not offered in expressions |
Call not initialized, or initialized before a change | Initialize again |
| Bursts of 429 in the backend run | Interval too short, or other workflows on the same key | Use 1 second or more, or a second key |
What it costs
On SimpleVerifier, API access is included in the Pro plan at $29.99 a month, with unlimited email verification and no per-call credits. Email + Phone at $49.99 a month adds live carrier lookups through the same API, if your app collects phone numbers too. If you only need to clean your existing users once, pay-as-you-go packs cost $9 for 2,500 or $19 for 10,000 emails by CSV upload. See pricing.
The short version
Add the API to Bubble's API Connector with Private key in header, set to Bearer plus your key. Create a POST Action call with an email body parameter and tick Include errors in response. In the sign-up workflow, verify first, show the reason and terminate when the status is invalid, and otherwise sign the user up and save the result. Verify existing users with Schedule API workflow on a list at a 1-second interval, or upload a CSV for big user bases.
Common questions
Does Bubble verify email addresses?
Not on its own. Confirming that a mailbox exists needs a DNS lookup and a conversation with the recipient's mail server. In Bubble, you get that by calling an email verification API from the API Connector and branching on the status it returns.
Is my API key safe in Bubble's API Connector?
Yes, if you use Private key in header or tick Private on the header. Bubble's docs say private values stay on the server and are never sent to the browser. Never put the key in an option set, a page element, a custom state or an initialization default value.
Should the verify call be Data or Action?
Action. A Data call fetches when an element needs it, which can mean a call every time a page renders. An Action call runs once, at the step you choose, such as before Sign the user up.
What happens to a sign-up if the verification API is down?
With Include errors in response ticked, the call returns an error object instead of stopping the workflow. The status field is empty, so the condition status is not invalid is true and the sign-up goes ahead.
How fast can a Bubble backend workflow verify my existing users?
As fast as the interval you set. At 1 second between scheduled workflows, that is about 60 users a minute, or 3,600 an hour. The API allows 120 calls a minute per key, but leaving room protects live sign-ups that share the key.
Verify unlimited addresses for $29.99/month
Real SMTP mailbox checks. No credits, no per-email fees.
Get Started