List Transactions
Retrieve a paginated list of transactions with optional filters for date range, currency, and status.
List Transactions
GET /v1/transactions
Returns a cursor-paginated list of transactions for your account, ordered newest first. Page size is fixed at 10 per page.
Request
npm install @e-pay/nodenpm install @e-pay/nestjspip install epaypip install epaypip install epaypip install epaycomposer require epay-et/php-sdkcomposer require epay-et/php-sdkimplementation("com.epayethiopia:epay-java:0.1.0")implementation("com.epayethiopia:epay-spring-boot-starter:0.1.0")go get github.com/epay-et/go-sdkNothing NestJS-specific here — this is the same Node.js client call.
Nothing Django-specific here — this is the same Python client call.
Nothing Flask-specific here — this is the same Python client call.
Nothing FastAPI-specific here — this is the same Python client call.
Nothing Laravel-specific here — this is the same PHP client call.
Nothing Spring Boot-specific here — this is the same Java client call.
# All transactions
curl "https://api.epayethiopia.com/v1/transactions" \
-H "Authorization: Bearer sk_live_your_key_here"
# With filters
curl "https://api.epayethiopia.com/v1/transactions?status=completed¤cy=ETB&from=2026-08-01&to=2026-08-31" \
-H "Authorization: Bearer sk_live_your_key_here"
# Next page
curl "https://api.epayethiopia.com/v1/transactions?cursor=eyJjcmVhdGVkQXQi..." \
-H "Authorization: Bearer sk_live_your_key_here"
import { Epay } from '@e-pay/node';
const epay = new Epay({ apiKey: process.env.EPAY_SECRET_KEY });
// Iterating a page walks every following page, fetching lazily and
// carrying the filters along — no cursor bookkeeping.
for await (const transaction of await epay.transactions.list({
status: 'completed',
currency: 'ETB',
from: '2026-08-01',
to: '2026-08-31',
})) {
console.log(transaction.reference, transaction.amount);
}
// Or drive it a page at a time:
const page = await epay.transactions.list();
page.data; // exactly this page, no extra requests
page.hasMore; // boolean
await page.next(); // the next TransactionPage, or null
// Or cap the walk:
const recent = await (await epay.transactions.list()).toArray(50);const params = new URLSearchParams({
status: 'completed',
currency: 'ETB',
from: '2026-08-01',
to: '2026-08-31',
});
const res = await fetch(`https://api.epayethiopia.com/v1/transactions?${params}`, {
headers: { Authorization: `Bearer ${process.env.EPAY_SECRET_KEY}` },
});
if (!res.ok) throw new Error(`ePay ${res.status}: ${await res.text()}`);
const { data, nextCursor, hasMore } = await res.json();
// Feed nextCursor back as `cursor` to walk the following pages.
from datetime import date
from epay import Epay
epay = Epay() # reads EPAY_SECRET_KEY
# Iterating a page walks every following page, fetching lazily and
# carrying the filters along — no cursor bookkeeping.
for transaction in epay.transactions.list(
status="completed",
currency="ETB",
from_date=date(2026, 8, 1),
to_date=date(2026, 8, 31),
):
print(transaction["reference"], transaction["amount"])
# Or drive it a page at a time:
page = epay.transactions.list()
page.data # exactly this page, no extra requests
page.has_more # bool
page.next_page() # the next TransactionPage, or None
# Or cap the walk:
recent = epay.transactions.list().to_list(limit=50)import os
import requests
res = requests.get(
"https://api.epayethiopia.com/v1/transactions",
headers={"Authorization": f"Bearer {os.environ['EPAY_SECRET_KEY']}"},
params={
"status": "completed",
"currency": "ETB",
"from": "2026-08-01",
"to": "2026-08-31",
},
timeout=30,
)
res.raise_for_status()
body = res.json()
transactions = body["data"]
next_cursor = body["nextCursor"]
has_more = body["hasMore"]
# Feed next_cursor back as `cursor` to walk the following pages.
use Epay\Epay;
$epay = new Epay(); // reads EPAY_SECRET_KEY
// Iterating a page walks every following page, fetching lazily and
// carrying the filters along — no cursor bookkeeping.
foreach ($epay->transactions->list([
'status' => 'completed',
'currency' => 'ETB',
'from' => '2026-08-01',
'to' => '2026-08-31',
]) as $transaction) {
echo $transaction['reference'], ' ', $transaction['amount'], PHP_EOL;
}
// Or drive it a page at a time:
$page = $epay->transactions->list();
$page->data(); // exactly this page, no extra requests
$page->hasMore(); // bool
$page->nextPage(); // the next TransactionPage, or null
// Or cap the walk:
$recent = $epay->transactions->list()->toArray(50);$query = http_build_query([
'status' => 'completed',
'currency' => 'ETB',
'from' => '2026-08-01',
'to' => '2026-08-31',
]);
$ch = curl_init("https://api.epayethiopia.com/v1/transactions?{$query}");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('EPAY_SECRET_KEY')],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($status >= 400) {
throw new RuntimeException("ePay {$status}: {$body}");
}
$page = json_decode($body, true);
// $page['data'], $page['nextCursor'], $page['hasMore']
// Iterate walks every page for you, fetching lazily and carrying the
// filters along — no cursor bookkeeping.
it := client.Transactions.Iterate(epay.ListParams{
Status: epay.StatusCompleted,
Currency: "ETB",
From: "2026-08-01",
To: "2026-08-31",
})
for it.Next(ctx) {
transaction := it.Transaction()
log.Println(transaction.Reference, transaction.Amount)
}
if err := it.Err(); err != nil {
return err
}
// Or drive the cursor yourself:
page, err := client.Transactions.List(ctx, epay.ListParams{})
page.Transactions // exactly this page
page.HasMore // bool
next, err := page.Next(ctx) // nil when this was the last pagequery := url.Values{
"status": {"completed"},
"currency": {"ETB"},
"from": {"2026-08-01"},
"to": {"2026-08-31"},
}
req, \_ := http.NewRequestWithContext(ctx, http.MethodGet,
"https://api.epayethiopia.com/v1/transactions?"+query.Encode(), nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("EPAY_SECRET_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode >= 400 {
return fmt.Errorf("ePay %d", res.StatusCode)
}
var page struct {
Data []struct {
Reference string `json:"reference"`
Status string `json:"status"`
Amount string `json:"amount"`
} `json:"data"`
NextCursor \*string `json:"nextCursor"`
HasMore bool `json:"hasMore"`
}
if err := json.NewDecoder(res.Body).Decode(&page); err != nil {
return err
}
// Feed page.NextCursor back as `cursor` to walk the following pages.
Epay epay = Epay.fromEnvironment(); // reads EPAY_SECRET_KEY
ListParams filters = ListParams.builder()
.status(TransactionStatus.COMPLETED)
.currency("ETB")
.from(LocalDate.of(2026, 8, 1))
.to(LocalDate.of(2026, 8, 31))
.build();
// Iterating a page walks every following page, fetching lazily and
// carrying the filters along — no cursor bookkeeping.
for (Transaction transaction : epay.transactions().list(filters)) {
System.out.println(transaction.reference() + " " + transaction.amount());
}
// Or drive it a page at a time:
TransactionPage page = epay.transactions().list();
page.data(); // exactly this page, no extra requests
page.hasMore(); // boolean
page.nextPage(); // Optional<TransactionPage>
// Or cap the walk:
List<Transaction> recent = epay.transactions().list().toList(50);HttpClient http = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder(
URI.create("https://api.epayethiopia.com/v1/transactions?status=completed¤cy=ETB&from=2026-08-01&to=2026-08-31"))
.header("Authorization", "Bearer " + System.getenv("EPAY_SECRET_KEY"))
.GET()
.build();
HttpResponse<String> response = http.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() >= 400) {
throw new IllegalStateException("ePay " + response.statusCode() + ": " + response.body());
}
// {"data":[…],"nextCursor":"…","hasMore":true} — feed nextCursor back as `cursor`.
String page = response.body();import { Controller, Get, Query } from '@nestjs/common';
import { EpayService, type TransactionStatus } from '@e-pay/nestjs';
@Controller('payments')
export class PaymentsController {
constructor(private readonly epay: EpayService) {}
// GET /payments?status=completed&cursor=…
@Get()
async list(
@Query('status') status?: TransactionStatus,
@Query('cursor') cursor?: string,
) {
const page = await this.epay.transactions.list({ status, cursor });
return {
data: page.data,
nextCursor: page.nextCursor,
hasMore: page.hasMore,
};
}
}from django.http import JsonResponse
from django.views.decorators.http import require_GET
from .clients import epay # Epay(api_key=settings.EPAY_SECRET_KEY)
@require_GET
def payments(request):
# GET /payments?status=completed&cursor=…
page = epay.transactions.list(
status=request.GET.get("status"),
cursor=request.GET.get("cursor"),
)
return JsonResponse(
{"data": page.data, "nextCursor": page.next_cursor, "hasMore": page.has_more}
)from flask import jsonify, request
from app import app, epay # epay = Epay(), one per process
@app.get("/payments")
def payments():
# GET /payments?status=completed&cursor=…
page = epay.transactions.list(
status=request.args.get("status"),
cursor=request.args.get("cursor"),
)
return jsonify(data=page.data, nextCursor=page.next_cursor, hasMore=page.has_more)from typing import Optional
from app.main import app, epay # epay = AsyncEpay(), closed in lifespan
@app.get("/payments")
async def payments(status: Optional[str] = None, cursor: Optional[str] = None):
page = await epay.transactions.list(status=status, cursor=cursor)
return {"data": page.data, "nextCursor": page.next_cursor, "hasMore": page.has_more}use Epay\Epay;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
final class PaymentController
{
public function __construct(private readonly Epay $epay) {}
// GET /payments?status=completed&cursor=…
public function index(Request $request): JsonResponse
{
$page = $this->epay->transactions->list(array_filter([
'status' => $request->query('status'),
'cursor' => $request->query('cursor'),
]));
return response()->json([
'data' => $page->data(),
'nextCursor' => $page->nextCursor(),
'hasMore' => $page->hasMore(),
]);
}
}@RestController
@RequestMapping("/payments")
class PaymentController {
private final Epay epay;
PaymentController(Epay epay) {
this.epay = epay;
}
record PageResponse(List<Transaction> data, String nextCursor, boolean hasMore) {}
// GET /payments?status=completed&cursor=…
@GetMapping
PageResponse list(
@RequestParam(required = false) String status,
@RequestParam(required = false) String cursor) {
ListParams.Builder filters = ListParams.builder();
if (status != null) filters.status(TransactionStatus.fromValue(status));
if (cursor != null) filters.cursor(cursor);
TransactionPage page = epay.transactions().list(filters.build());
return new PageResponse(page.data(), page.nextCursor().orElse(null), page.hasMore());
}
}Response
{
"data": [
{
"reference": "PAB12CD3420260813",
"merchantReference": "order_123",
"status": "completed",
"amount": "250.00",
"currencyCode": "ETB",
"paidAt": "2026-08-13T11:47:00.000Z",
"createdAt": "2026-08-13T11:30:00.000Z"
},
{
"reference": "PFF98AB1220260812",
"merchantReference": null,
"status": "pending",
"amount": "1500.00",
"currencyCode": "ETB",
"paidAt": null,
"createdAt": "2026-08-12T09:15:00.000Z"
}
],
"nextCursor": "eyJjcmVhdGVkQXQiOiIyMDI2LTA4LTEyVDA5OjE1OjAwLjAwMFoiLCJpZCI6ImZmOThhYjEyIn0",
"hasMore": true
}Query Parameters
Prop
Type
Response
Prop
Type
Transaction object
Prop
Type
Pagination
This endpoint uses cursor-based pagination with a fixed page size of 10.
First page Make a request without a cursor. You'll receive up to 10
transactions and a nextCursor if more exist.
Subsequent pages Pass the nextCursor from the previous response as the
cursor query parameter. Filters persist across pages.
Last page When hasMore is false and nextCursor is null, you've
reached the end.
Cursors are opaque and time-bound. Do not parse or construct them manually — always use the value returned by the API.
Errors
| Status | Description |
|---|---|
400 | from is after to. |
400 | Date range exceeds 90 days. |
400 | currency is not a valid 3-letter code. |
400 | status is not a recognized value. |
400 | cursor is malformed or expired. |
401 | Missing or invalid API key. |
Next: Retrieve a Transaction — fetch full details for a single transaction.