Transactions

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

raw HTTP
npm install @e-pay/node
npm install @e-pay/nestjs
pip install epay
pip install epay
pip install epay
pip install epay
composer require epay-et/php-sdk
composer require epay-et/php-sdk
implementation("com.epayethiopia:epay-java:0.1.0")
implementation("com.epayethiopia:epay-spring-boot-starter:0.1.0")
go get github.com/epay-et/go-sdk

Nothing 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&currency=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 page
query := 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&currency=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.

Cursors are opaque and time-bound. Do not parse or construct them manually — always use the value returned by the API.


Errors

StatusDescription
400from is after to.
400Date range exceeds 90 days.
400currency is not a valid 3-letter code.
400status is not a recognized value.
400cursor is malformed or expired.
401Missing or invalid API key.

Next: Retrieve a Transaction — fetch full details for a single transaction.

On this page