Forex
Lock in a price for converting between the Philippine Peso and supported foreign currencies, and settle against that price from anywhere in the PayMongo platform.
Overview
PayMongo Forex is the pricing layer behind every cross-currency money movement on the platform. It fetches live exchange rates from trusted venues, applies a transparent markup, and issues short-lived quotes — time-bound price offers that downstream PayMongo products (Transfers, Wallets, Disbursements) can settle against with confidence that the rate will not move under them.
Forex runs on the same money movement infrastructure as the rest of PayMongo, so you integrate against it with the same API keys, Dashboard, and tooling you already use for Payments and Transfers.
What you can do
- Get a live exchange rate for a supported currency pair at any moment.
- Create a quote — a locked-in price, good for a short window, that downstream services can settle against.
- Retrieve the exact rate that was live at a past moment in time, for audit and reconciliation.
- Operate on individually negotiated pricing — merchants with a custom rate automatically receive their own markup.
- See both the original market price and the price you will actually transact at on every quote, so the spread is transparent.
Supported currency pairs
| Target currency | Base currency | Best for |
|---|---|---|
| USDT | PHP | Stablecoin transfers, settlement, and on-ramp flows |
| USDC | PHP | Stablecoin transfers, settlement, and on-ramp flows |
Additional currencies and corridors are on the roadmap and can be added upon request!
Pricing
- Every quote includes a transparent markup on top of the underlying market rate. A standard markup is applied before the quote is returned.
- Quotes are returned with both the marked-up price (what you will transact at) and the original market price (before markup), so you always see the spread.
- Custom rates are available for eligible merchants. Contact your PayMongo account manager or [email protected] to request a preferential markup.
- Quotes are free to create and retrieve — you only pay when the downstream transfer, disbursement, or wallet transaction that references the quote actually settles, under that product's normal pricing.
Before you start
Before you create your first quote, make sure you have:
- An activated PayMongo account with Forex enabled for your business — if the capability is not visible in your Dashboard, reach out to [email protected].
- A supported currency pair in mind — currently
USDT/PHPorUSDC/PHP. Unsupported pairs are rejected before any pricing call is made. - Valid API keys for the environment you are targeting (test mode and live mode).
- A clear view of which direction you want to transact — see Buy vs. sell below.
Understanding the concepts
Rate
A rate is a snapshot of the market price for a currency pair at a specific moment in time, from a specific liquidity venue. Every rate has two legs:
- Buy price — the price at which the venue is willing to buy the target currency from you.
- Sell price — the price at which the venue is willing to sell the target currency to you.
Rates are continuously captured so you can always look up the price that was live at any past moment — useful for reconciliation, reporting, or investigating a discrepancy.
Quote
A quote is a locked-in price offer derived from a live rate. Creating a quote does three things:
- Fetches the current market rate for the pair.
- Applies the appropriate markup — default for most merchants, custom for merchants with a negotiated rate.
- Persists the result with an expiry timestamp, currently 60 seconds from creation.
Once issued, the quote's price is fixed. Downstream services (for example, a cross-currency disbursement) reference the quote ID to settle at exactly the price you were shown — even if the market moves in the meantime. If the quote expires before you settle, create a new one.
Buy vs. sell
Every quote has a side:
| Side | Meaning | quantity is in… | You receive… |
|---|---|---|---|
buy | You are buying the target currency using the base currency. | base_currency | The amount of target currency your base-currency spend will get you. |
sell | You are selling the target currency in exchange for base currency. | quote_currency | The amount of base currency your target-currency sale will get you. |
A worked example for USDT/PHP:
- Buy side, quantity 1,000: "I have 1,000 PHP. How many USDT can I get?" The quote returns
quote_quantityin USDT. - Sell side, quantity 50: "I have 50 USDT to sell. How many PHP do I get?" The quote returns
base_quantityin PHP.
Original price vs. marked-up price
Every quote returns two prices:
price— the rate you will actually transact at, after markup. All quantities on the quote are calculated using this price.original_price— the underlying market price before markup. Provided for transparency and reconciliation.
The difference between the two is the spread PayMongo (or your custom agreement) applies.
Creating a quote through the API
Forex is currently an API-first capability — there is no Dashboard flow for creating quotes by hand. For developers building conversion workflows, quoting follows a simple pattern:
- Create a quote for the pair, side, and quantity you need.
- Read back the price and quantities you will transact at, and surface them to your user.
- Settle against the quote — pass the
quote_idto the downstream PayMongo service (for example, a disbursement or a wallet transaction) beforeexpires_atpasses. - Refresh if needed — if the quote expires before you settle, create a new one. Quotes cannot be extended.
A typical create-quote request:
POST /v1/quotes
{
"side": "buy",
"quote_currency": "USDT",
"base_currency": "PHP",
"quantity": 1000
}For the full request and response schema, see the Quote resource in the PayMongo Developer Documentation.
A typical create-quote response:
{
"data": {
"id": "quote_AYD5d5e5J2NsHL15zy4ord4V",
"side": "buy",
"price": 56.4075,
"original_price": 55.85,
"quote_currency": "USDT",
"quote_quantity": 17.7281,
"base_currency": "PHP",
"base_quantity": 1000,
"created_at": "2026-04-23T10:00:00Z",
"expires_at": "2026-04-23T10:01:00Z"
}
}Key fields to watch:
price— the marked-up rate your transaction will execute at. Use this for display and for any quantity math downstream.original_price— the market rate before markup. Use this for reconciliation and spread reporting.expires_at— the hard deadline for settling against this quote. After this, the quote is gone and a new one must be created.quote_quantity/base_quantity— the two sides of the trade. Which one you should display depends on thesideyou requested.
Retrieving a quote
You can look up a quote by ID at any time. If the quote has expired, the API returns a gone response — treat this as a signal to create a new quote, not as a retryable error.
Retrieving rates
You can query a historical rate for any supported pair at any past moment. Given a timestamp, Forex returns the most recent rate that was captured at or before that time, so you can confirm what the market looked like when an earlier transaction was priced.
Test mode provides a simulated environment with no real fund movement so you can validate your integration end-to-end — from quote creation to downstream settlement — before going live.
For the full request and response schema, see the Rate resource in the PayMongo Developer Documentation.
Tracking a quote's lifecycle
A quote only has two meaningful states:
| Status | Meaning |
|---|---|
active | The quote exists and expires_at is still in the future. It can be referenced by downstream services to lock in the price. |
expired | expires_at has passed. The quote is visible for audit but can no longer be settled against. Retrieving it returns a gone response — create a fresh quote. |
Quotes do not emit webhooks today. Settlement events — "the transfer that referenced this quote succeeded / failed" — are surfaced by the downstream product (Transfers, Disbursements, Wallets) under their own webhook streams, keyed by the quote_id you passed in.
Webhooks for quote-level events (consumed, expired-before-use) are on the roadmap.
Error handling
Failed requests return a standard error envelope:
{
"errors": [
{
"code": "unprocessable_entity",
"message": "rate for currency pair is not found in provider"
}
]
}Errors fall into two groups: request errors caused by your payload or account state, and pricing errors caused by upstream market conditions.
Common request errors
| Error code | Cause | What to do |
|---|---|---|
invalid_query_parameters | Query parameters missing or malformed on a rate lookup | Check that target_currency, base_currency, provider, and timestamp are all set. |
invalid_request_body | Quote request body invalid or malformed | Verify the payload shape and types against the API reference. |
parameter_required | A required field is missing | Add the field named in the error message and retry. |
unauthorized | API key missing or incorrect | Confirm you are using the right key for the environment (test vs. live). |
not_found | Quote or rate ID does not exist | Double-check the ID — it may belong to a different environment. |
gone | Quote has expired | Create a fresh quote and reference the new ID. |
unprocessable_entity | Unsupported currency pair, same currency on both sides, or market pricing unavailable | Check the pair is supported and retry pricing errors after a short backoff. |
internal_server_error | Unexpected server error | Retry after a short delay. If it persists, contact support with the request ID. |
Pricing-level errors
Occasionally the underlying liquidity venue is unreachable or has no rate to quote for a supported pair. These are surfaced as unprocessable_entity with a message explaining the pair is not currently priced. They are almost always transient — retry after a short backoff before surfacing an error to your end user.
Retry guidance
activequotes that are close to expiring should be treated as single-use. Do not try to "refresh" a quote by re-reading it — create a new one.goneis never retryable on the same ID. The price is off the table and a new quote is required.- Transient
unprocessable_entityon pricing (as opposed to capability validation) is safe to retry after a second or two. 500 internal_server_erroris safe to retry with backoff; escalate if it persists.- Quote creation is not idempotent today — avoid blind retries on request timeouts. Prefer surfacing the failure and letting the user trigger a new attempt, or tolerate duplicate quotes (they are cheap and expire quickly).
Cross-border and additional corridors (coming soon)
Forex today covers PHP ↔ USDT and PHP ↔ USDC via a single liquidity venue. Additional capability is on the roadmap.
Planned scope (subject to change):
- Additional stablecoin and fiat pairs.
- Multi-venue routing with best-price selection across liquidity providers.
- Quote-level webhooks for consumption and expiry events.
- Idempotency keys on quote creation.
- Dashboard views for browsing live rates, historical rates, and issued quotes.
If cross-border forex is on your roadmap and you would like to be notified when additional corridors open in beta, reach out to your PayMongo account manager.
Related reading
- Disbursements — how to move money out of PayMongo, including flows that will settle against a Forex quote.
- Wallets — how the PayMongo Wallet funds and receives conversions.
- Ledgers and Workflows — how the two legs (base and quote) of a settled conversion are recorded and automated.
- Webhooks — how to configure reliable event delivery for downstream settlement events tied to a quote.
Updated about 4 hours ago