Connect FlutterFlow to Binance API by using plain API Calls for public market data (no key needed) and a Firebase Cloud Function or Supabase Edge Function for private account endpoints that require HMAC-SHA256 signing. Never put your Binance secret in the app — it ships in the bundle and exposes every account action.
| Fact | Value |
|---|---|
| Tool | Binance API |
| Category | Finance & Accounting |
| Method | Custom Action (Dart) |
| Difficulty | Advanced |
| Time required | 60 minutes |
| Last updated | July 2026 |
Binance API in FlutterFlow: Public Data is Easy, Private Data Needs a Proxy
Binance exposes a well-documented REST API at `https://api.binance.com` that splits neatly into two tiers. Public endpoints — current prices (`/api/v3/ticker/price`), candlestick data (`/api/v3/klines`), order book depth — require only an `X-MBX-APIKEY` header or nothing at all. These are safe to call from a plain FlutterFlow API Call because there is no secret involved. Private endpoints — account balances, trade history, placing or cancelling orders — require every request to carry a `signature` parameter computed as an HMAC-SHA256 hash of the full query string using your API secret key.
That signing requirement is the critical FlutterFlow design constraint. FlutterFlow compiles to a Flutter app that runs on the user's device. Any value you place in an API Call header, a Dart Custom Action, or App Values is embedded in the compiled app bundle and can be extracted with standard reverse-engineering tools. An exposed Binance secret lets anyone place trades, withdraw funds, or read private data as your account. The safe architecture is to keep the secret in a Firebase Cloud Function or Supabase Edge Function, compute the signature there, and have FlutterFlow call your proxy instead of Binance directly.
Binance's API is free to use; trading fees apply per transaction. The public market-data tier allows approximately 1,200 request-weight units per minute (each endpoint costs 1–40 weight units — verify current weights in the Binance API docs, as they change). Build a crypto price dashboard with public calls first, then add private portfolio features through the proxy.
Integration method
Public Binance endpoints (price tickers, klines, depth) are consumed via plain FlutterFlow API Calls with no authentication. Private endpoints (balances, order history, order placement) require every request to be signed with HMAC-SHA256 over the query string using your API secret — that signing must happen inside a Firebase Cloud Function or Supabase Edge Function, never in the Flutter app bundle. A Dart Custom Action or a simple API Call then hits your proxy instead of Binance directly.
Prerequisites
- A Binance account with an API key generated (Settings → API Management) — use read-only permissions for a dashboard
- A FlutterFlow project (any plan that supports API Calls and Custom Code)
- A Firebase project with Cloud Functions enabled (Blaze plan) OR a Supabase project for the signing proxy
- Basic understanding of FlutterFlow API Calls and Data Types
Step-by-step guide
Generate a Binance API key with minimal permissions
Log into Binance and open the API Management page (top-right account menu → API Management). Click 'Create API', choose 'System Generated', give it a label like 'FlutterFlow Dashboard', and complete the 2FA verification. On the permissions screen, enable only 'Enable Reading' — do not enable 'Enable Spot & Margin Trading', 'Enable Withdrawals', or any other permission unless your specific app genuinely requires them. The principle of least privilege limits the damage if the key is ever exposed. After creation you will see the API Key (also called the API Key or `apiKey`) and the Secret Key. Copy the Secret Key immediately — Binance only shows it once. Store the Secret Key in your Firebase/Supabase secrets environment (not in a text file, not in your code). Store the API Key somewhere accessible but not embedded in the app; you will put it in the proxy environment too. You can restrict the API key by IP allowlist (enter your Firebase Cloud Functions or Supabase Edge Function's egress IP) — this prevents the key from being used from any other server even if it leaks. Check the Binance API Management page for instructions on setting IP restrictions.
Pro tip: Use read-only permissions unless you are building order-placement features. IP-restrict the key to your proxy server's IP for an extra safety layer.
Expected result: You have an API Key and a Secret Key. The Secret Key is saved securely in your Firebase/Supabase environment variables, not anywhere in the FlutterFlow project.
Add public market-data API Calls in FlutterFlow
In FlutterFlow, open the left navigation and click 'API Calls'. Click '+ Add' and select 'Create API Group'. Name it 'Binance Market Data', set the base URL to `https://api.binance.com`, and leave the headers empty for now — public endpoints need no authentication. Click '+ Add API Call' inside the group. For a price ticker call: name it 'Get Ticker Price', set the method to GET, and set the endpoint to `/api/v3/ticker/price`. Click the 'Variables' tab and add a variable named `symbol` with a test value of `BTCUSDT`. Back in the endpoint field, reference it as `/api/v3/ticker/price?symbol={{symbol}}`. Click 'Response & Test', press 'Test API Call', and Binance will return something like `{"symbol": "BTCUSDT", "price": "65000.00"}`. Click 'Generate from Response' to create the JSON paths `$.symbol` and `$.price` automatically. For kline data: add another API Call named 'Get Klines', endpoint `/api/v3/klines?symbol={{symbol}}&interval={{interval}}&limit={{limit}}`, with variables `symbol`, `interval` (e.g. `1h`), and `limit` (e.g. `50`). The kline response is an array of arrays — add JSON paths `$[*][0]` for open time, `$[*][1]` for open, `$[*][2]` for high, `$[*][3]` for low, `$[*][4]` for close. These public calls fire from the device with no secret. Add them to a page's Backend Query or trigger them from an Action Flow.
1{2 "api_group": "Binance Market Data",3 "base_url": "https://api.binance.com",4 "calls": [5 {6 "name": "Get Ticker Price",7 "method": "GET",8 "endpoint": "/api/v3/ticker/price?symbol={{symbol}}",9 "variables": [10 { "name": "symbol", "type": "String", "test_value": "BTCUSDT" }11 ],12 "json_paths": {13 "symbol": "$.symbol",14 "price": "$.price"15 }16 },17 {18 "name": "Get 24h Ticker",19 "method": "GET",20 "endpoint": "/api/v3/ticker/24hr?symbol={{symbol}}",21 "variables": [22 { "name": "symbol", "type": "String", "test_value": "BTCUSDT" }23 ],24 "json_paths": {25 "priceChangePercent": "$.priceChangePercent",26 "lastPrice": "$.lastPrice",27 "volume": "$.volume"28 }29 }30 ]31}Pro tip: Cache the ticker response in an App State variable and only refresh it on a 30–60 second timer. Each ticker call costs 2 request-weight units — frequent refreshes across many symbols burn the ~1,200/min budget quickly.
Expected result: The API Call test returns live price data from Binance. You can see JSON paths for symbol and price in the Response panel.
Deploy a Firebase Cloud Function as the HMAC-SHA256 signing proxy
Private Binance endpoints require the query string to be signed with your API secret using HMAC-SHA256. This must happen on a server, not in the Flutter app. Set up a Firebase Cloud Function (Node.js) that receives requests from FlutterFlow, adds the timestamp and signature, and forwards to Binance. In your Firebase project (Blaze plan required for outbound HTTP), create a function named `binanceProxy`. Store your Binance API key in Firebase Functions config or Secret Manager as `BINANCE_API_KEY` and your secret as `BINANCE_API_SECRET`. The function reads these at runtime — they never touch the Flutter app. The function's job: (1) receive the path and query parameters from FlutterFlow, (2) append `timestamp` (current server milliseconds — server time avoids clock-skew errors), (3) compute the HMAC-SHA256 signature over the entire query string, (4) forward the signed request to Binance with the `X-MBX-APIKEY` header, and (5) return the response JSON to FlutterFlow. Deploy with `firebase deploy --only functions` from your local machine. The deployed function URL becomes the base URL of a new FlutterFlow API Group named 'Binance Private'. Your FlutterFlow app never sees the API key or secret — it only calls your proxy URL.
1// Firebase Cloud Function — binanceProxy (index.js)2const functions = require('firebase-functions');3const https = require('https');4const crypto = require('crypto');56const BINANCE_API_KEY = process.env.BINANCE_API_KEY;7const BINANCE_SECRET = process.env.BINANCE_API_SECRET;8const BASE_URL = 'https://api.binance.com';910exports.binanceProxy = functions.https.onRequest(async (req, res) => {11 res.set('Access-Control-Allow-Origin', '*');12 if (req.method === 'OPTIONS') { res.status(204).send(''); return; }1314 const path = req.query.path || '/api/v3/account';15 const timestamp = Date.now();16 const params = { ...req.query, timestamp };17 delete params.path;1819 const queryString = Object.keys(params)20 .map(k => `${k}=${params[k]}`)21 .join('&');22 const signature = crypto23 .createHmac('sha256', BINANCE_SECRET)24 .update(queryString)25 .digest('hex');2627 const url = `${BASE_URL}${path}?${queryString}&signature=${signature}`;2829 const response = await fetch(url, {30 headers: { 'X-MBX-APIKEY': BINANCE_API_KEY }31 });32 const data = await response.json();33 res.json(data);34});Pro tip: Use Firebase Secret Manager (not Functions config) for production — it encrypts secrets at rest and supports rotation. Set the `recvWindow` parameter (e.g. 5000ms) in the proxy if you see timestamp-rejection errors.
Expected result: The Firebase Cloud Function is deployed and accessible at a URL like `https://us-central1-yourproject.cloudfunctions.net/binanceProxy`. Calling it with `?path=/api/v3/account` returns your Binance account balances.
Add a private-data API Call in FlutterFlow pointing to your proxy
Now create a second API Group in FlutterFlow for private (signed) Binance data. Click 'API Calls' → '+ Add' → 'Create API Group'. Name it 'Binance Private Proxy', and set the base URL to your Firebase Cloud Function URL (e.g. `https://us-central1-yourproject.cloudfunctions.net/binanceProxy`). You do not add any authentication headers here — the proxy handles that server-side. Add an API Call named 'Get Account Balances'. Set the method to GET and the endpoint to just a trailing `/` or empty (the proxy URL itself is the endpoint). Add a variable named `path` with a default value of `/api/v3/account`. The proxy reads `?path=` from the query string and forwards to Binance. In the Response & Test tab, paste a sample Binance account response and click 'Generate from Response' to create JSON paths for the balances array: `$.balances[*].asset`, `$.balances[*].free`, `$.balances[*].locked`. Add this API Call to a FlutterFlow page as a Backend Query, filtering out zero-balance assets in a local variable or in the proxy before returning. Bind the asset name and free balance to a List widget — each row shows a coin and its available balance. For trade history: add another API Call named 'Get My Trades' with `?path=/api/v3/myTrades&symbol={{symbol}}`. Pass the symbol from a page variable set by the user tapping a coin in the balance list.
1{2 "api_group": "Binance Private Proxy",3 "base_url": "https://us-central1-yourproject.cloudfunctions.net/binanceProxy",4 "calls": [5 {6 "name": "Get Account Balances",7 "method": "GET",8 "endpoint": "/?path=/api/v3/account",9 "json_paths": {10 "balances": "$.balances",11 "asset": "$.balances[*].asset",12 "free": "$.balances[*].free",13 "locked": "$.balances[*].locked"14 }15 },16 {17 "name": "Get My Trades",18 "method": "GET",19 "endpoint": "/?path=/api/v3/myTrades&symbol={{symbol}}",20 "variables": [21 { "name": "symbol", "type": "String", "test_value": "BTCUSDT" }22 ]23 }24 ]25}Pro tip: Filter zero-balance coins in the proxy (e.g. `balances.filter(b => parseFloat(b.free) > 0)`) before returning to FlutterFlow — it reduces payload size and avoids binding empty list rows.
Expected result: The 'Get Account Balances' API Call returns real balance data when tested. FlutterFlow shows the JSON paths for asset, free, and locked fields, and you can bind them to a List widget.
Cache price data in App State and bind everything to the dashboard
With both API Groups wired up, build the dashboard screen. Create an App State variable named `lastTickerFetchMs` (Integer) and `tickerList` (a list of your Ticker Data Type with fields: symbol String, price String, changePercent String). On the dashboard page, add an Action triggered by the page load: check whether `currentTime - lastTickerFetchMs > 30000` (30 seconds). If true, fire the 'Get Ticker Price' API Call for each symbol in the watchlist (use a loop variable or multiple parallel calls), update `tickerList` in App State, and set `lastTickerFetchMs` to current time. If false, skip the API call and use the cached App State values. Add a Periodic Action (available in FlutterFlow's Action Flow Editor under 'Timer') set to 30 seconds to trigger the same refresh logic while the screen is open. This avoids burning through Binance's ~1,200 request-weight budget on rapid-fire calls. For the balance section, trigger the 'Get Account Balances' proxy call on page load and store the result in a Page State variable. Add a pull-to-refresh gesture to the List widget that re-triggers the balance call. Bind the ticker list to a ListView: symbol Text widget, price Text widget formatted as a number, and changePercent Text widget with conditional styling (green if the value starts without '-', red otherwise). Bind the balance list to a separate ListView with asset and free amount. The dashboard is now live.
Pro tip: Binance returns all prices as strings (e.g. '65000.12') — use FlutterFlow's 'Format Value' on the Text widget to display with a fixed number of decimal places rather than parsing in Dart.
Expected result: The dashboard screen shows live coin prices refreshing every 30 seconds from public API Calls, and account balances fetched through the proxy. Pull-to-refresh updates the balance list.
Handle 429/418 rate-limit errors and clock-skew rejections
Binance returns HTTP 429 when you exceed request-weight limits and HTTP 418 when the IP is temporarily banned for ignoring 429s. Both require a back-off strategy. In your Firebase Cloud Function proxy, wrap the Binance fetch in a try/catch and forward the status code to FlutterFlow rather than swallowing it. In FlutterFlow, add an error handler in the Action Flow after each API Call: check if the response status code is 429 or 418 and display a Snackbar with 'Rate limit reached — please wait a moment before refreshing'. Clock-skew errors appear as HTTP 400 with a body containing `Timestamp for this request is outside the recvWindow` — add a `recvWindow=5000` parameter in the proxy's query string to give a 5-second tolerance window. The proxy uses `Date.now()` (server time) for the timestamp, which avoids device-clock drift entirely. Signature errors appear as HTTP 400 with `Signature for this request is not valid` — these usually mean the query-string parameters were ordered or encoded differently between the proxy's signing step and the forwarded request. Log the exact query string and signature in the proxy during testing to verify they match. For the web build: Binance's API does not allow browser-origin requests (CORS is blocked). All API Calls from FlutterFlow web — even public ones — must go through the proxy. For native mobile builds, public API Calls work directly; only private calls need the proxy.
Pro tip: Add a `Retry-After` header check in the proxy: if Binance returns 429, read the `Retry-After` header value (seconds) and include it in the error response so the FlutterFlow UI can show a meaningful countdown.
Expected result: Rate-limit and signature errors are caught and surfaced as user-friendly messages. The proxy logs show correct query-string signing, and timestamp errors no longer appear after adding recvWindow.
Common use cases
Crypto portfolio tracker showing real-time prices
A FlutterFlow app that displays a user's watchlist of coin pairs with live prices, 24-hour change, and a sparkline chart. Public API Calls fetch ticker data on a 30-second timer; the results are stored in App State and bound to a List widget with color-coded percentage changes.
Build a crypto price dashboard that shows BTC/USDT, ETH/USDT, and BNB/USDT with live prices updated every 30 seconds, displaying 24-hour percentage change in green or red.
Copy this prompt to try it in FlutterFlow
Personal trading history and balance viewer
A private mobile app for a trader's own accounts that shows spot balances and recent trade history. A Firebase Cloud Function holds the API key and secret, signs each request, and returns account data. FlutterFlow calls the proxy and binds the response to a balance list and trade history table.
Show my Binance spot balances and last 10 trades for a selected coin pair. Refresh balances with a pull-to-refresh gesture.
Copy this prompt to try it in FlutterFlow
Candlestick chart for technical analysis
A research tool that fetches kline (candlestick) data from the public Binance API and renders it in a chart. A Custom Widget wraps a pub.dev charting package; the data comes from a plain FlutterFlow API Call to `/api/v3/klines` with interval and symbol variables.
Fetch 1-hour kline data for ETH/USDT for the last 50 candles and render a candlestick chart with open, high, low, close values.
Copy this prompt to try it in FlutterFlow
Troubleshooting
HTTP 400 — 'Signature for this request is not valid'
Cause: The HMAC-SHA256 signature was computed over a different query string than the one sent to Binance, or the timestamp is outside the recvWindow. Common causes: query parameters are in a different order between the signing step and the forwarded URL, or the secret has a trailing space/newline.
Solution: Log the exact query string in the proxy both before signing and in the forwarded URL — they must be identical. Trim the secret key when reading it from environment variables (`process.env.BINANCE_API_SECRET.trim()`). Add `recvWindow=5000` to the signed parameters.
HTTP 429 or HTTP 418 — too many requests or IP ban
Cause: The FlutterFlow app is refreshing price tickers too frequently and has exceeded the ~1,200 request-weight per minute limit. HTTP 418 indicates Binance has temporarily banned the proxy server's IP for ignoring 429 warnings.
Solution: Cache ticker data in App State for at least 30–60 seconds. Add rate-limit handling in the proxy to return a structured error when Binance responds with 429. Stop all calls immediately on 418 and wait at least 5 minutes before retrying. Check current weight costs per endpoint in the Binance API documentation.
XMLHttpRequest error / CORS blocked on web build
Cause: Binance's API servers block browser-origin requests. FlutterFlow web builds run in a browser and hit CORS restrictions even for public endpoints.
Solution: Route all Binance calls through the Firebase Cloud Function proxy for web builds. The proxy adds an `Access-Control-Allow-Origin: *` header and forwards to Binance server-to-server, bypassing the browser's CORS enforcement. Native iOS/Android builds are not affected by CORS.
HTTP 401 — 'Invalid API-key, IP, or permissions for action'
Cause: The API key is incorrect, has been deleted/regenerated on the Binance side, the endpoint requires a permission the key doesn't have, or the IP allowlist on the Binance API key doesn't include the proxy server's IP.
Solution: Verify the API key value in the proxy environment variables matches the key shown in Binance API Management. If you set an IP allowlist on the Binance key, add the Firebase Cloud Functions outbound IP (check GCP for the range). Confirm the key has the permissions needed for the endpoint (e.g. 'Enable Reading' for account data).
Best practices
- Never put the Binance API secret in FlutterFlow API Call headers, App Values, or Dart Custom Actions — it ships in the compiled app bundle and can be extracted.
- Use read-only permissions when generating the Binance API key for a dashboard app; only enable trading permissions if the app genuinely places orders.
- IP-restrict your Binance API key to the Firebase Cloud Functions or Supabase Edge Function outbound IP to limit blast radius if the key leaks.
- Cache public ticker data in App State for 30–60 seconds and use Periodic Actions instead of triggering an API Call on every widget rebuild.
- Use server-side timestamps (Date.now() in the proxy) for signing, never device timestamps — device clocks drift and cause signature rejections.
- Verify current request-weight costs in the Binance API documentation before choosing polling intervals — weights change across API versions.
- For web builds, route all calls (including public ones) through the proxy to avoid CORS blocks; for native mobile, public calls can go direct.
- Filter zero-balance assets in the proxy response rather than in FlutterFlow to keep payload sizes small and list widgets clean.
Alternatives
Coinbase offers a more beginner-friendly API with OAuth user authentication, making it better suited for apps where end users log into their own Coinbase accounts.
If you need payment processing in your app rather than crypto trading data, Stripe is the standard choice with a FlutterFlow-native integration path.
For reading bank and brokerage account balances without executing trades, Plaid is the compliant, officially-supported aggregation alternative to raw exchange APIs.
Frequently asked questions
Can I put the Binance API secret in a FlutterFlow App Value or Custom Action?
No. App Values are compiled into the app bundle and can be read by decompiling the APK or IPA. Custom Action Dart code is also compiled into the binary. Anyone who installs your app can extract the secret and use it to trade or withdraw funds from your account. Always keep the secret in a Firebase Cloud Function or Supabase Edge Function.
Can I use Binance WebSocket streams in FlutterFlow for real-time prices?
FlutterFlow API Calls are HTTP request/response — they cannot hold open a WebSocket connection. For real-time streaming, you would need a Dart Custom Action using the `web_socket_channel` package from pub.dev. Be aware that custom actions do not run in FlutterFlow's web Test Mode — you must test on a physical device or APK build. For most dashboards, polling every 30 seconds via API Calls is simpler and sufficient.
Do I need the Binance API key for public market data like prices and klines?
No. Public endpoints including `/api/v3/ticker/price`, `/api/v3/klines`, and `/api/v3/depth` work without any authentication. You can call these directly from FlutterFlow API Calls without a proxy. The API key (as `X-MBX-APIKEY` header) is optional but raises the rate-limit tier; the secret key is only required for signed private endpoints.
What happens if Binance changes its API endpoints?
Because all private calls go through your Firebase/Supabase proxy, you only need to update the proxy code — not redeploy the FlutterFlow app. For public calls configured directly in FlutterFlow API Calls, you would need to update the endpoint in the API Call settings and publish a new app version. This is another reason to route all calls through a centralized proxy where possible.
Does this integration work for FlutterFlow web builds?
Public Binance endpoints block browser-origin requests (CORS), so even public calls will fail in a FlutterFlow web build if pointed directly at `api.binance.com`. Route all calls through the Firebase proxy for web builds — the proxy makes server-to-server requests that bypass browser CORS restrictions. Native iOS and Android builds are not affected by CORS.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation