Listar Ofertas P2P
Obtener el listado paginado de ofertas P2P disponibles con filtros avanzados.
GET /p2p
Retorna el listado de ofertas P2P disponibles, con soporte para filtros por tipo, moneda, monto, ratio, estado y paginación.
Autenticación
Bearer Token (Authorization: Bearer <token>) o Credenciales de App (app-id + app-secret en headers).
Cuando se usa Credenciales de App, la consulta se realiza en nombre del usuario propietario de la app. Dicho usuario debe cumplir los requisitos de acceso a P2P (p2p_enabled, KYC verificado, Telegram vinculado y teléfono verificado).
Request
Con Bearer Token:
curl -X GET "https://api.qvapay.com/p2p?take=20&page=1&type=buy&coin=BANK_CUP" \
-H "Authorization: Bearer <token>"Con Credenciales de App:
curl -X GET "https://api.qvapay.com/p2p?take=20&page=1&type=buy&coin=BANK_CUP" \
-H "app-id: {tu-app-uuid}" \
-H "app-secret: {tu-app-secret}"Parámetros (query string)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
page | number | No | Página a consultar (por defecto 1) |
take | number | No | Resultados por página (por defecto 20, máximo 100) |
type | string | No | Filtrar por tipo: buy o sell (alias: offer_type) |
coin | string | No | Filtrar por tick de la moneda (ej: BANK_CUP) |
orderBy | string | No | Campo de ordenamiento: updated_at (por defecto), created_at, amount, receive, ratio, best_rate, rating, trades |
orderType | string | No | Orden: asc o desc (por defecto desc; alias: order). Con orderBy=best_rate, desc es "mejor tasa primero" |
my | boolean | No | true o 1 muestra solo las ofertas del usuario autenticado (como dueño o como peer) |
peer | string | No | Filtrar por usuario: acepta UUID o username (con o sin @) |
sortByStatus | boolean | No | Solo con my=true: agrupa por estado (revisión → procesando → pagado → abierta → completada). Por defecto true |
min | number | No | Monto mínimo (alias: min_price) |
max | number | No | Monto máximo (alias: max_price) |
ratio_min | number | No | Ratio mínimo (receive / amount) (alias: min_ratio) |
ratio_max | number | No | Ratio máximo (receive / amount) (alias: max_ratio) |
only_vip | boolean | No | true o 1 muestra solo ofertas marcadas como exclusivas para usuarios VIP |
search | string | No | Búsqueda general (detalles, mensaje, uuid, montos). Solo funciona junto a my=true |
status | string | No | Filtrar por estado: open, revision, processing, paid, completed, cancelled. Solo aplica junto a my=true (el listado público siempre es open) |
Los ordenamientos ratio (tasa receive / amount), best_rate, rating (valoración promedio del ofertante) y trades (operaciones P2P completadas del ofertante) son rankings globales sobre el listado público; en my=true caen a updated_at (salvo ratio, que sí se aplica sobre tus ofertas).
ratio vs best_rate
ratio es la tasa cruda receive / amount (moneda por USD) y no es lo mismo que "mejor tasa": su dirección se invierte según el lado del mercado, porque el ratio se mide siempre desde la oferta, no desde quien la toma.
| Tipo de oferta | Quien la toma… | El ratio es… | Mejor para quien la toma |
|---|---|---|---|
sell | compra QUSD pagando la moneda | lo que paga por dólar | ratio más bajo |
buy | vende QUSD y recibe la moneda | lo que recibe por dólar | ratio más alto |
Por eso orderBy=ratio&orderType=desc sobre un listado type=sell devuelve las ofertas más caras primero. Usa orderBy=best_rate, que normaliza el signo según el tipo: orderType=desc es siempre "mejor primero" y asc "peor primero", en ambos lados.
best_rate requiere type y coin (responde 400 si faltan): sin tipo el signo no está definido, y los ratios de monedas distintas no son comparables entre sí (1000 CUP/USD vs 1.2 MLC/USD ordenaría por moneda, no por tasa). Tampoco aplica con my=true, donde la perspectiva es la del dueño de la oferta.
# Mejores ofertas para comprar CUP por transferencia bancaria
curl -X GET "https://api.qvapay.com/p2p?type=sell&coin=BANK_CUP&orderBy=best_rate&orderType=desc" \
-H "Authorization: Bearer {token}"Response
Paginación estilo Laravel. amount, receive y los demás campos decimales se serializan como strings.
{
"current_page": 1,
"data": [
{
"uuid": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"type": "buy",
"coin": "BANK_CUP",
"amount": "50",
"receive": "12500",
"payment_type_id": 1,
"message": "Pago rápido",
"private": false,
"only_vip": false,
"status": "open",
"created_at": "2024-06-20T14:30:00.000Z",
"updated_at": "2024-06-20T14:30:00.000Z",
"offer_kind": "fixed",
"available_amount": "50",
"reserved_amount": "0",
"order_min": null,
"order_max": null,
"User": {
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"username": "johndoe",
"name": "John",
"image": "https://example.com/avatar.png",
"kyc": true,
"vip": false,
"golden_check": false,
"phone_verified": true,
"telegram_verified": true,
"rating_avg": 4.85,
"rating_count": 12,
"_count": {
"P2P": 34,
"P2P_Peer": 18
}
},
"Coin": {
"name": "Transferencia CUP",
"tick": "BANK_CUP",
"logo": "https://example.com/cup.png",
"price": "1"
}
}
],
"per_page": 20,
"total": 143
}Notas:
_count.P2Py_count.P2P_Peerson las operaciones P2P completadas del usuario como dueño y como peer, respectivamente.- En ofertas con peer asignado (visible con
my=true) se incluye un objetoPeercon la misma estructura queUser. - Con
my=truesinstatusexplícito, la respuesta incluye"sorted_by_status": truey las ofertas llegan agrupadas por estado.
Errores
| Código | Descripción |
|---|---|
400 | Parámetro inválido (page, take, orderBy, orderType, type, status o filtros numéricos), orderBy=best_rate sin type/coin, o el usuario no cumple los requisitos de acceso a P2P |
401 | Token de autenticación inválido o ausente |
429 | Demasiadas solicitudes (límite: 30 cada 60 segundos por usuario) |