Connecting Bubble to Magento uses a permanent Integration Token in a Private Bearer Authorization header — not the Admin token, which expires every 4 hours. The defining Bubble challenge is Magento's searchCriteria filter system: bracket-notation URL parameters like `searchCriteria[filterGroups][0][filters][0][field]=status` must be added as individual parameters in Bubble's API Connector, not collapsed into a single query string. Each bracket-key is a separate parameter entry.
| Fact | Value |
|---|---|
| Tool | Magento |
| Category | E-commerce |
| Method | Bubble API Connector |
| Difficulty | Advanced |
| Time required | 2–4 hours |
| Last updated | July 2026 |
Magento for Bubble — Integration Tokens, searchCriteria syntax, and enterprise-scale considerations
Magento is the most complex e-commerce API covered in this guide. It is designed for enterprise operations — multi-store catalogs, B2B pricing tiers, configurable products with child SKUs, and a filter query system that looks like no other API. Bubble founders encounter it when a client's existing store is on Magento and they need to build a custom UI or operations tool on top of it.
The authentication choice matters immediately. Magento offers Admin user tokens (POST to `/V1/integration/admin/token` with username and password) — these expire after 4 hours and require your admin credentials in the request body, which is a security risk. The better choice for Bubble is an **Integration Token**: created in Magento's System → Extensions → Integrations panel with scoped resource permissions, it is permanent (does not expire) and uses the principle of least privilege — you choose exactly which APIs the token can access.
During Integration activation, Magento generates four token values: Consumer Key, Consumer Secret, Access Token, and Access Token Secret. Only the **Access Token** is used as the Bearer token in Bubble's API Connector. Using Consumer Key or Consumer Secret instead causes a 401 error with no explanation.
The searchCriteria filter system is Magento's most distinctive API characteristic. Instead of `?status=pending`, Magento uses `?searchCriteria[filterGroups][0][filters][0][field]=status&searchCriteria[filterGroups][0][filters][0][value]=pending&searchCriteria[filterGroups][0][filters][0][conditionType]=eq`. In Bubble's API Connector, each bracket-key must be a separate URL parameter — you cannot combine them into one parameter. This means a filter with 3 criteria requires adding 9+ individual parameter entries in the call editor.
For multi-store Magento, the store code is a URL path segment — `/rest/default/V1/` for the default store or `/rest/{store_code}/V1/` for other stores. Parameterize this in Bubble using a state variable for the store code.
Integration method
Bubble API Connector with a Magento Integration Token in a Private Bearer Authorization header; Magento's bracket-notation searchCriteria URL parameters added individually in the API Connector call editor.
Prerequisites
- Magento Open Source or Adobe Commerce installation with admin access (System → Extensions → Integrations menu available)
- Your Magento store's domain and the REST API base URL (typically https://yourstore.com/rest)
- The API Connector plugin installed in your Bubble app (Plugins tab → Add plugins → search 'API Connector' → Install)
- A list of the Magento REST API resources your Bubble app needs (orders, catalog, customers, inventory) — you'll scope these in the Integration settings
- For Adobe Commerce Cloud: confirmation from your IT team that Bubble's server IP ranges are whitelisted at the WAF/CDN layer
Step-by-step guide
Create a Magento Integration and obtain the Access Token
Log in to your Magento admin panel at `yourstore.com/admin`. In the left sidebar, navigate to System → Extensions → Integrations. Click 'Add New Integration.' Fill in: - **Name**: give it a descriptive name like 'Bubble App Integration' - **Email**: enter a contact email (for notifications if the integration is revoked) - **Current User Identity Verification**: you may need to enter your admin password to confirm identity Switch to the 'API' tab in the Integration form. This is where you scope the token's access: - Under 'Resource Access,' select 'Custom' - Check the resources your Bubble app needs. For an order management dashboard: Sales (Orders, Shipments, Invoices), Catalog (Products, Categories), Customer (Customers). Do not grant 'All' unless absolutely required — least-privilege scoping reduces risk. Click 'Save.' Magento brings you back to the Integrations list — find your new integration and click 'Activate.' Magento shows the 'Integration Tokens for Extensions' popup with four values: 1. Consumer Key 2. Consumer Secret 3. **Access Token** ← this is the one you need 4. Access Token Secret Copy only the **Access Token** (the third value in the popup). This is what you paste into Bubble as the Bearer token. Consumer Key and Consumer Secret are for OAuth-based integrations — using them as Bearer causes 401 errors. Click 'Done' to close the popup. If you lose the Access Token, you must deactivate and re-activate the integration to generate new tokens — there is no 'show token' option.
1// Magento Integration activation generates 4 values:2// 1. Consumer Key: Use only for OAuth-based integrations3// 2. Consumer Secret: Use only for OAuth-based integrations4// 3. Access Token: ← USE THIS as the Bearer token in Bubble5// 4. Access Token Secret: Use only for OAuth-based integrations67// Correct Bearer format in Bubble API Connector:8// Authorization: Bearer [Access Token value] // mark Private910// Base REST API URL formats:11// Single default store: https://yourstore.com/rest/default/V1/12// Multi-store by code: https://yourstore.com/rest/{store_code}/V1/13// All stores (admin): https://yourstore.com/rest/all/V1/1415// Verify access (test from browser with curl):16// curl -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \17// https://yourstore.com/rest/default/V1/store/storeConfigs18// Expected: JSON array of store configuration objectsPro tip: When selecting API scopes for the Integration, start with read-only access first (check only 'Get' operations under each resource). Test your Bubble integration fully. Then return to the Integration settings to add write access (PUT, POST operations) only for the specific resources your workflows need to modify. This staged approach prevents accidental data changes during development.
Expected result: The Magento Integration is activated and shows 'Active' status in System → Extensions → Integrations. You have copied the Access Token (not Consumer Key or Consumer Secret). The token is ready to paste into Bubble's API Connector.
Configure the Bubble API Connector for Magento
In your Bubble editor, go to Plugins → API Connector. Click 'Add another API' and name it 'Magento.' Set the base URL to `https://yourstore.com/rest` — this stops at `/rest`, not including the store scope or `/V1/`. The store scope and version go in each individual call's URL path. In the shared headers section for this API group, add: - Header name: `Authorization`, Value: `Bearer YOUR_ACCESS_TOKEN` — paste your Access Token after 'Bearer ', then check the **Private** checkbox - Header name: `Content-Type`, Value: `application/json` - Header name: `Accept`, Value: `application/json` Now add the Initialize Call. Click 'Add another call' and name it 'Get Store Configs.' Set method GET and URL path `/default/V1/store/storeConfigs`. This endpoint returns store configuration objects — safe to use as Initialize because it is read-only and returns useful data (store name, website ID, locale, base_currency_code). Click Initialize call. Magento returns a JSON array of store configuration objects. Bubble detects the array fields. If the Initialize call returns a 401, your Access Token is wrong (check you are using Access Token, not Consumer Key). If it returns 403, the Integration scope does not include Store configuration — add 'Stores' under API scopes and re-activate. For multi-store Magento: add a state variable `magento_store_code` (text, default 'default') on your Bubble pages and reference it in each call's URL path. In the URL field, type `/` + the state variable + `/V1/products` etc. Bubble evaluates the URL dynamically per workflow run.
1// Bubble API Connector: Magento2// Base URL: https://yourstore.com/rest3// (do NOT include /default/V1/ here — it goes in each call)4//5// Shared Headers:6// Authorization: Bearer YOUR_ACCESS_TOKEN // mark Private7// Content-Type: application/json8// Accept: application/json9//10// Initialize Call: GET /default/V1/store/storeConfigs11//12// Expected response:13// [14// {15// "id": 1,16// "code": "default",17// "website_id": 1,18// "locale": "en_US",19// "base_currency_code": "USD",20// "default_display_currency_code": "USD",21// "timezone": "America/Chicago",22// "weight_unit": "lbs",23// "base_url": "https://yourstore.com/",24// "base_link_url": "https://yourstore.com/",25// "base_static_url": "https://yourstore.com/pub/static/version1/",26// "base_media_url": "https://yourstore.com/pub/media/"27// }28// ]Pro tip: Set the base URL to just `https://yourstore.com/rest` — not `/rest/V1/` or `/rest/default/V1/`. Keeping the version and store scope in individual call paths makes it easier to switch between store scopes (default, specific store code, 'all') and API versions without reconfiguring the entire connector.
Expected result: The Magento API Connector initializes successfully. The Initialize Call returns an array of store config objects with locale, currency, and URL fields. The Authorization header shows the Access Token marked Private in the shared headers.
Build order list and filter calls using Magento's searchCriteria syntax
Magento's list endpoints use a bracket-notation filter system called searchCriteria. Unlike standard REST APIs where you pass `?status=pending`, Magento requires a nested bracket structure: ``` ?searchCriteria[filterGroups][0][filters][0][field]=status &searchCriteria[filterGroups][0][filters][0][value]=pending &searchCriteria[filterGroups][0][filters][0][conditionType]=eq ``` In Bubble's API Connector, these cannot be passed as a single URL parameter — you must add each bracket-key as a **separate URL parameter entry** in the call editor. Add a new call named 'Get Orders.' Set method GET, URL `/default/V1/orders`. Now add each filter parameter as individual entries: 1. Parameter: `searchCriteria[filterGroups][0][filters][0][field]`, Value: `status` (or make it dynamic) 2. Parameter: `searchCriteria[filterGroups][0][filters][0][value]`, Value: `pending` (dynamic) 3. Parameter: `searchCriteria[filterGroups][0][filters][0][conditionType]`, Value: `eq` 4. Parameter: `searchCriteria[currentPage]`, Value: `1` (dynamic) 5. Parameter: `searchCriteria[pageSize]`, Value: `20` (dynamic) 6. Parameter: `searchCriteria[sortOrders][0][field]`, Value: `created_at` 7. Parameter: `searchCriteria[sortOrders][0][direction]`, Value: `DESC` For the Initialize call, fill all dynamic fields with real values (status=pending, page=1, etc.) so Magento returns actual order data. Magento order list response: `{ "items": [ {...order objects...} ], "total_count": 247, "search_criteria": {...} }`. Map `items` in the API Connector to get the order list. Use `total_count` to build pagination — divide by pageSize and create a page number state variable. Magento's conditionType values: `eq` (equals), `neq` (not equals), `like` (contains, use `%value%`), `gteq` (greater than or equal), `lteq` (less than or equal), `in` (any of, comma-separated values), `notnull` (field exists). Standard operators like `>=` or `contains` do not work and return empty results without an error.
1// Magento → Get Orders2// Method: GET3// URL: /default/V1/orders4//5// URL Parameters (each as a separate entry in Bubble's API Connector):6// searchCriteria[filterGroups][0][filters][0][field] = status7// searchCriteria[filterGroups][0][filters][0][value] = pending (dynamic)8// searchCriteria[filterGroups][0][filters][0][conditionType] = eq9// searchCriteria[currentPage] = 1 (dynamic)10// searchCriteria[pageSize] = 2011// searchCriteria[sortOrders][0][field] = created_at12// searchCriteria[sortOrders][0][direction] = DESC13//14// To add a date range filter (add as filterGroups[1]):15// searchCriteria[filterGroups][1][filters][0][field] = created_at16// searchCriteria[filterGroups][1][filters][0][value] = 2026-01-01 00:00:0017// searchCriteria[filterGroups][1][filters][0][conditionType] = gteq18//19// Response:20// {21// "items": [22// {23// "base_grand_total": 59.98,24// "grand_total": 59.98,25// "customer_email": "john@example.com",26// "customer_firstname": "John",27// "customer_lastname": "Doe",28// "increment_id": "000000042",29// "status": "pending",30// "created_at": "2026-01-27 14:32:00",31// "items": [ { "sku": "WT09-XS-Purple", "qty_ordered": 2, "row_total": 59.98 } ]32// }33// ],34// "total_count": 24735// }Pro tip: filterGroups use AND logic between groups, OR logic within a group's filters array. To filter orders that are EITHER pending OR processing, use filterGroups[0][filters][0] for pending and filterGroups[0][filters][1] for processing — both in the same group. To filter orders that are pending AND created after a date, use filterGroups[0] for status and filterGroups[1] for the date — separate groups.
Expected result: The Get Orders call initializes successfully, returning an items array with order objects and a total_count. Bubble detects the nested fields: increment_id, customer_email, grand_total, status, and created_at. A Repeating Group bound to this call shows a list of Magento orders filtered by the status parameter.
Build product catalog calls and handle configurable product stock
Add a call named 'Get Products' for the product catalog. Method GET, URL `/default/V1/products`. Add searchCriteria parameters: - `searchCriteria[filterGroups][0][filters][0][field]` = `status` - `searchCriteria[filterGroups][0][filters][0][value]` = `1` (enabled products only) - `searchCriteria[filterGroups][0][filters][0][conditionType]` = `eq` - `searchCriteria[pageSize]` = `20` - `searchCriteria[currentPage]` = `1` (dynamic) Magento product response: `{ "items": [{...}], "total_count": N, "search_criteria": {...} }`. Each product has `sku`, `name`, `price`, `type_id` (simple, configurable, grouped, virtual), `status`, `weight`, and a `custom_attributes` array. For **configurable products** (products with size/color variations): the parent configurable product has a NULL or zero price and NULL stock — price and stock live on child Simple products. To get child SKUs: GET `/default/V1/configurable-products/{sku}/children`. Each child has its own price and stock quantity. For stock quantity: GET `/default/V1/stockStatuses?searchCriteria[filterGroups][0][filters][0][field]=sku&searchCriteria[filterGroups][0][filters][0][value]={sku}&searchCriteria[filterGroups][0][filters][0][conditionType]=eq`. The response includes `items[0].qty` and `items[0].stock_status` (1=in stock, 0=out of stock). For a product price update: PUT `/default/V1/products/{sku}` with a body containing the complete product object — Magento requires full-object PUT, not partial updates. Fetch the product first with GET `/default/V1/products/{sku}`, modify the price field in a Bubble state, then PUT the complete modified object. For bulk price updates across many products, use 'Schedule API Workflow on a list' in Bubble to avoid the 30-second timeout.
1// Magento → Get Products2// Method: GET3// URL: /default/V1/products4//5// URL Parameters:6// searchCriteria[filterGroups][0][filters][0][field] = status7// searchCriteria[filterGroups][0][filters][0][value] = 18// searchCriteria[filterGroups][0][filters][0][conditionType] = eq9// searchCriteria[pageSize] = 2010// searchCriteria[currentPage] = 1 (dynamic)11//12// Response (items array structure):13// {14// "items": [15// {16// "id": 2048,17// "sku": "WT09-XS-Purple",18// "name": "Breathe-Easy Tank",19// "price": 34.00,20// "type_id": "simple",21// "status": 1,22// "visibility": 4,23// "custom_attributes": [24// { "attribute_code": "description", "value": "..." },25// { "attribute_code": "color", "value": "Purple" }26// ]27// }28// ],29// "total_count": 185430// }3132// Get configurable product children (for sizes/colors):33// GET /default/V1/configurable-products/{parent_sku}/children34// Returns array of simple product objects with individual price + stock3536// Get stock for a specific SKU:37// GET /default/V1/stockStatuses?searchCriteria[filterGroups][0][filters][0][field]=sku38// &searchCriteria[filterGroups][0][filters][0][value]={sku}39// &searchCriteria[filterGroups][0][filters][0][conditionType]=eqPro tip: Magento's `custom_attributes` array is a list of `{ attribute_code: string, value: any }` objects. To display a specific custom attribute in Bubble, filter the `custom_attributes` list by `attribute_code` in your Bubble expression. For example, to get product description, find the item in the list where attribute_code = 'description' and take its value.
Expected result: The Get Products call initializes with an items array of Magento products including id, sku, name, price, and type_id. A Repeating Group in Bubble shows the product catalog. For a configurable product, the children call returns an array of simple products each with their own price, stock, and attribute values.
Update order status using POST /orders/{id}/comments
Updating an order's status in Magento from Bubble has a simpler path than most guides describe. The correct approach is NOT a PUT to the full `/V1/orders/{id}` endpoint (which requires a complete order object body). Instead, POST to `/V1/orders/{id}/comments` with a `statusHistory` body — this is the same endpoint Magento's own admin panel uses when a staff member changes an order status. Add a call named 'Update Order Status.' Set method POST, URL `/default/V1/orders/<id>/comments` where `<id>` is a dynamic text parameter for the Magento order ID (the numeric `entity_id`, not the `increment_id` display number). Set the body type to JSON. The required body structure: ```json { "statusHistory": { "comment": "Status updated from Bubble", "is_customer_notified": 0, "is_visible_on_front": 0, "parent_id": null, "status": "complete" } } ``` Set `is_customer_notified` to `0` to suppress customer email notifications during batch updates or testing; set to `1` when you want Magento to email the customer about the status change. For the Initialize call, you need a real Magento order ID (use one from your test orders list). The response is just `true` on success. For Bubble workflow: when a warehouse operator clicks 'Mark Shipped' on an order in your dashboard Repeating Group, use a workflow action: 'Plugins → API Connector → Magento → Update Order Status' with the entity_id from the current cell's data and status='complete'. Add a second workflow step to refresh the Repeating Group data or update the cell's displayed status via a custom state.
1// Magento → Update Order Status2// Method: POST3// URL: /default/V1/orders/<id>/comments4// (<id> = dynamic: Magento order entity_id, e.g. 1234)5//6// Body (JSON):7{8 "statusHistory": {9 "comment": "<dynamic: optional internal note>",10 "is_customer_notified": 0,11 "is_visible_on_front": 0,12 "parent_id": null,13 "status": "<dynamic: new status string>"14 }15}1617// Common Magento order status values:18// pending → new order, not yet processed19// processing → payment confirmed, being fulfilled20// complete → shipped and delivered21// closed → refunded/returned22// canceled → canceled before fulfillment23// holded → held for manual review24// payment_review → payment flagged for review2526// Note: use the order's entity_id (numeric, e.g. 1234)27// NOT the increment_id (display number, e.g. 000001234)28// The entity_id is in the 'id' field of the GET /V1/orders responsePro tip: Magento's increment_id (the customer-facing order number like #000001234) is different from the entity_id (the internal numeric ID like 1234). The /orders/{id}/comments endpoint requires the entity_id. In Bubble, when you load orders with GET /V1/orders, both fields are in the response — store the `id` field (entity_id) in your Repeating Group cell's data for use in status update calls.
Expected result: The Update Order Status call returns `true` on success. In Magento admin, the order shows the new status and an internal comment entry in the order history. The call completes in under 2 seconds for individual orders.
Handle Adobe Commerce Cloud IP whitelisting and test the full integration
Before going live, test the full Bubble-to-Magento integration and address Adobe Commerce Cloud-specific requirements if applicable. **Adobe Commerce Cloud WAF:** Adobe Commerce Cloud (Magento hosted by Adobe) includes a Web Application Firewall (WAF) and CDN that may block API calls from unrecognized IP ranges — including Bubble's server IPs. If your API calls succeed with curl from your local machine but fail with HTTP 403 from Bubble, IP whitelisting is likely the cause. Contact your Magento system administrator or Adobe Commerce support to whitelist Bubble's IP ranges. As Bubble uses a multi-tenant infrastructure, the specific IP ranges vary — Bubble publishes current server IP ranges in their documentation. Request whitelisting of these ranges at the CDN/WAF layer, not just at the Magento application level. **End-to-end testing checklist:** 1. In Bubble's Logs tab → Workflow logs, verify all GET calls return 200 with populated data 2. Test searchCriteria filters by changing status values and verifying the returned orders match the filter 3. Test the order status update on a sandbox or staging Magento environment before production 4. For multi-store setups, verify that switching the store_code state variable returns products from the correct store 5. Test pagination: set currentPage to 2 and verify different products appear 6. Check Bubble's Workload Unit consumption in the Logs — complex searchCriteria calls and bulk product loads consume significant WU on high-traffic pages For complex enterprise Magento integrations — multi-source inventory, B2B quote management, ERP sync — RapidDev's team has experience with Magento API architecture at scale. Reach out at rapidevelopers.com/contact for a scoping call.
1// Multi-store Magento: switch store scope dynamically2// Add a Dropdown on the page, data source = API Connector → Magento → Get Store Configs3// Display field: code4// On change: Set state 'magento_store_code' to Dropdown's value5//6// In all API Connector call URLs, reference the state:7// URL: /<magento_store_code state>/V1/orders8// URL: /<magento_store_code state>/V1/products9//10// Bubble Logs → check for WU consumption per call:11// - Simple GET /orders (1 page): ~15-30 WU12// - GET /products (large catalog): ~20-50 WU per page13// - POST /orders/{id}/comments: ~10-20 WU14//15// Adobe Commerce Cloud IP whitelist request:16// Provide Bubble's server IP ranges to your Magento admin for WAF whitelisting17// Check Bubble documentation for current server IP ranges to whitelist18// WAF whitelisting is done at the CDN level (Fastly, Cloudflare), not Magento configPro tip: Use a Magento staging environment (most enterprise Magento installations have one) for all Bubble integration development. Staging environments have separate Integration Tokens — create a staging Integration with the same scopes as production. Switch credentials when going live. Never test write operations (POST, PUT, DELETE) on a production Magento store without a staging run first.
Expected result: All Magento API calls from Bubble return 200 with correct data. searchCriteria filters return orders matching the specified criteria. Order status updates post successfully and appear in Magento admin order history. Multi-store switching loads products from the correct Magento store view.
Common use cases
Custom Magento order management dashboard for store operators
Build a Bubble operations panel that gives your team filtered views of Magento orders by status, date range, and shipping region — with the ability to update order status, add notes, and mark as shipped — all without requiring Magento admin access. The Bubble interface can be customized for specific roles (warehouse staff, customer support, fulfillment) with tailored workflows.
On the dashboard page, bind a Repeating Group to the GET /default/V1/orders call with searchCriteria filters for status=processing and sort by created_at descending. Show each order's increment_id, customer_firstname, customer_lastname, grand_total, and status. Add a 'Mark Shipped' button that triggers POST /default/V1/orders/{id}/comments with status=complete and a tracking note.
Copy this prompt to try it in Bubble
Magento product catalog browser with multi-store support
Build a Bubble app for merchandising teams to browse and edit the Magento product catalog across multiple stores — viewing product names, descriptions, prices, and stock levels with the ability to filter by category and update pricing. Use a store-selector Dropdown in Bubble to switch between Magento store views dynamically.
Add a Dropdown to the page with values from GET /V1/store/storeConfigs (returns all store codes). Bind a Repeating Group to GET /{selected_store}/V1/products with searchCriteria filters for type_id=simple and status=1. Each cell shows sku, name, price, and qty. A price-edit input triggers PUT /{selected_store}/V1/products/{sku} with the updated price object.
Copy this prompt to try it in Bubble
Customer self-service portal for Magento shoppers
Build a custom Bubble customer portal where shoppers can view their Magento order history, track shipments, download invoices, and initiate returns — with a better mobile UX than Magento's default customer account pages. Authenticate customers via a dedicated customer token endpoint and use it for customer-scoped API calls.
On customer login, POST to /V1/integration/customer/token with the customer's email and password to get a customer token. Store it in a Bubble state. Use this token (instead of the Integration Token) in the Authorization header for GET /V1/orders/mine to load that customer's orders. Display order history, items, and tracking information.
Copy this prompt to try it in Bubble
Troubleshooting
Magento API returns 401 Unauthorized even though the Authorization header looks correct
Cause: You are using the Consumer Key or Consumer Secret as the Bearer token instead of the Access Token. Magento generates four token values during Integration activation; only the 'Access Token' (third in the list) is used as Bearer. Consumer Key and Consumer Secret are for OAuth-based request signing, which is a different authentication flow.
Solution: Go to Magento admin → System → Extensions → Integrations → find your Integration → click 'Edit.' If the integration is already activated, deactivate it and re-activate to see the popup with all four token values again. In the popup, the 'Access Token' field is the third row. Copy its value (a long alphanumeric string) and update the Bearer token in Bubble's API Connector Authorization header. The Consumer Key looks different — it's shorter and formatted differently from the Access Token.
'There was an issue setting up your call' on any Magento GET endpoint — no response fields detected
Cause: The Initialize call returned an error (401, 403, or 404 with an HTML error page) rather than valid JSON. Bubble cannot detect response schema from error responses. Common causes: Integration Token is wrong (see above), the Integration scope does not include the requested resource, or the API endpoint path is incorrect.
Solution: Test the API call directly in a browser or REST client first: GET `https://yourstore.com/rest/default/V1/store/storeConfigs` with `Authorization: Bearer YOUR_ACCESS_TOKEN`. If it returns a JSON response, the credentials are correct and the issue is with the specific endpoint path in Bubble. If it returns 401, the token is wrong. If it returns 403, the Integration scope does not include this resource — add it in System → Extensions → Integrations → Edit → API tab.
Magento searchCriteria filter returns empty items array even though matching orders exist
Cause: An incorrect conditionType value is being used. Magento uses Magento-specific conditionType strings: `eq`, `neq`, `like`, `gteq`, `lteq`, `in`, `notnull`. Using standard operators like `>=`, `contains`, `!=`, or `>` causes Magento to silently return empty results with no error — the conditionType is simply ignored or falls back to an unrecognized type.
Solution: Check every filter's conditionType parameter value in Bubble's API Connector. Replace any non-Magento operators with the correct Magento equivalents: use `eq` for exact match, `gteq` for 'greater than or equal', `lteq` for 'less than or equal', `like` with percent wildcards for partial text match (e.g., value=`%purple%`). Also verify that the filter field name exactly matches Magento's internal field name (e.g., `created_at` not `date_created`, `entity_id` not `id`).
Bubble API calls to Magento fail with 403 Forbidden when called from Bubble but succeed from local curl
Cause: Adobe Commerce Cloud's WAF (Web Application Firewall) is blocking requests from Bubble's server IP ranges. Adobe Commerce Cloud includes CDN and WAF layers (typically Fastly) that may not have Bubble's server IPs in their allowlist. This is specific to Adobe Commerce Cloud, not self-hosted Magento.
Solution: Contact your Magento/Adobe Commerce admin or Adobe Support to add Bubble's server IP ranges to the WAF allowlist at the CDN layer. Bubble's server IPs are available in Bubble's documentation. This is a network-level change, not a Magento configuration change — it must be done by someone with CDN/WAF admin access. As a temporary workaround during testing, a Cloudflare Worker proxy can forward Bubble's requests from a whitelisted IP.
Best practices
- Use a Magento Integration Token with the minimum required API scopes — not 'All Resources.' Scope the Integration to exactly the resources your Bubble app reads and writes: if your dashboard only shows orders and products, scope to Sales and Catalog only. This limits the blast radius if the token is ever compromised.
- Never use Admin user tokens (POST /V1/integration/admin/token with username/password) in Bubble integrations. Admin tokens expire after 4 hours and require embedding admin credentials in an API call — Integration Tokens are permanent, scoped, and do not expose admin passwords.
- Add Magento's base URL `/rest` as the API Connector base URL and put the store scope (`/default/V1/`) in each individual call path — not in the base URL. This allows you to parameterize the store code with a Bubble state variable for multi-store switching without duplicating the entire API Connector configuration.
- Always use `searchCriteria[pageSize]` and `searchCriteria[currentPage]` on all Magento list calls. Without pagination, Magento defaults to returning a small number of items or a very large dataset depending on the endpoint — large product or order responses can hit Bubble's response size limits and cause workflow timeouts.
- For order status updates, use POST /orders/{id}/comments (the statusHistory endpoint) rather than PUT /orders/{id}. The comments endpoint is simpler, does not require a full object body, and is what Magento's admin UI uses internally — it is less likely to cause unintended side effects on complex order objects.
- Test on a Magento staging environment before making any write operations on production. Magento staging environments should have identical Integration Token scope — create a separate Integration on staging with the same permissions. Test all searchCriteria filter combinations, pagination, and status updates on staging before pointing to production.
- Monitor Workload Unit consumption in Bubble's Logs tab. Magento calls with complex searchCriteria filters, large product catalogs, and nested response structures consume more WU than simple API calls. Cache read-heavy data (product catalog, category list) in Bubble Data Things to reduce per-pageload API calls.
Alternatives
WooCommerce uses simple Basic Auth with no filter syntax complexity — a GET /products call with standard `per_page` and `page` parameters, compared to Magento's bracket-notation searchCriteria system. WooCommerce is a WordPress plugin targeting small-to-mid stores; Magento targets enterprise multi-store operations. If your client's store is on WooCommerce, it is significantly faster to integrate with Bubble.
PrestaShop uses an API key as Basic Auth username (with empty password) and requires JSON format as an explicit URL parameter. Like Magento, it has a full-object PUT pattern for updates. PrestaShop is popular in Europe and LATAM; Magento is more common in North America and large global retailers. Both are enterprise-grade but Magento has a more complex filter system.
BigCommerce uses a simpler API Key + Client ID authentication header pair and standard page/limit pagination without bracket-notation filters. BigCommerce targets mid-market e-commerce with a SaaS model; Magento targets large enterprises with self-hosted or cloud deployments. BigCommerce's REST API is easier to work with in Bubble but requires a BigCommerce store.
Frequently asked questions
What is the difference between Magento Open Source and Adobe Commerce Cloud for this integration?
The Integration Token setup, searchCriteria syntax, and API endpoints work identically in both. The difference is infrastructure: Adobe Commerce Cloud (the hosted SaaS version) includes a CDN and WAF that may block Bubble's server IP ranges, requiring an IP whitelist request to Adobe. Self-hosted Magento Open Source has no WAF — Bubble calls reach Magento directly without IP restrictions. Also, Adobe Commerce (paid) includes additional API endpoints for B2B features, staging environments, and advanced inventory that Open Source does not have.
Can I use this integration on a Bubble free plan?
Yes for read-only operations (GET products, GET orders). The API Connector works on all Bubble plans. There is no backend workflow requirement for the Magento API Connector integration — all calls are outbound from Bubble, not inbound webhooks. The free plan limitation applies if you want to receive Magento webhooks in a Bubble Backend Workflow, but Magento's webhook feature is less commonly used — most Bubble integrations poll Magento rather than receiving pushes.
How do I filter products by category in Magento?
Magento product categories use category_id — not category name. First, fetch categories with GET /V1/categories to get the ID for the category you want. Then filter products with searchCriteria[filterGroups][0][filters][0][field]=category_id, value=your_category_id, conditionType=eq. Add this as individual parameters in the Bubble API Connector call. Note: category_id filtering uses Magento's EAV attribute system, so ensure the Integration scope includes Catalog Categories.
My Magento integration works on smaller product pages but times out on large catalogs. What should I do?
Magento's large product catalogs can return slow responses on complex filter queries, especially on shared hosting. In Bubble, reduce `searchCriteria[pageSize]` to 10-20 items per page — smaller pages are faster and more reliable. For bulk catalog operations (price updates, stock syncs), use Bubble's 'Schedule API Workflow on a list' to distribute work across multiple sequential workflow executions rather than one large single workflow. Also enable Magento's full-page cache if it is not already active — it significantly speeds up read operations.
Do I need a paid Bubble plan for this integration?
No — all outbound API Connector calls to Magento work on the Bubble free plan. The only feature that requires a paid plan is receiving inbound webhooks via Backend Workflows. Since Magento supports webhooks (available via plugins or custom modules), a paid plan is needed only if you want real-time push notifications from Magento to Bubble. For most use cases (dashboards, product browsers, order management), the free plan is sufficient.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation