Query Clearing Logs
Query all cleared transaction records to track settled transactions and reconcile card activity. Clearing logs represent final settled transactions that have been posted to card accounts, as opposed to authorization logs which show attempted transactions. Use this endpoint for financial reconciliation, accounting, expense reporting, and tracking actual card spending.
Resource Access
Production (api.ahrvo.network)
POST https://api.ahrvo.network/card/issuance/api/issuing/transaction/v2/clearings
Staging (gateway.ahrvo.network)
POST https://gateway.ahrvo.network/card/issuance/api/issuing/transaction/v2/clearings
Clearing Logs Overview
Clearing logs (also called settlement logs) show transactions that have moved from authorization to final settlement. Understanding the difference between authorizations and clearings is critical for accurate financial reporting:
Authorization vs. Clearing
| Aspect | Authorization | Clearing |
|---|---|---|
| Timing | Immediate when card is used | 1-3 days after authorization |
| Amount | May include hold amounts | Final settled amount |
| Status | Can be approved/declined | Only successful transactions |
| Purpose | Transaction approval | Actual fund movement |
| Balance Impact | Temporary hold | Permanent deduction |
Key Features
- Settlement Tracking: Monitor when authorizations convert to final charges
- Reconciliation: Match cleared transactions with accounting records
- Credit/Debit Classification: Distinguish between charges and refunds
- Cross-Border Fees: Track international transaction fees
- Posting Dates: Filter by settlement date vs. transaction date
- Authorization Linking: Connect clearings to original authorizations
Request Parameters
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
page_no | integer | No | Page number (starting from 1, default: 1) |
page_size | integer | No | Results per page (max 100, default: 20) |
card_id | string | No | Filter by specific card ID |
start_posting_date | string | No | Clearing from date (format: yyyy-MM-dd HH:mm:ss) |
end_posting_date | string | No | Clearing to date (format: yyyy-MM-dd HH:mm:ss) |
start_created_date | string | No | Transaction creation from date (format: yyyy-MM-dd HH:mm:ss) |
end_created_date | string | No | Transaction creation to date (format: yyyy-MM-dd HH:mm:ss) |
type | string | No | Transaction type: Credit or Debit |
Header Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
Authorization | string | Yes | Bearer token for authentication |
x-api-key | string | Yes | API key for authentication |
Response
Response Body
| Field | Type | Description |
|---|---|---|
code | string | Status code ('SUCCESS' for successful query) |
data.total_num | integer | Total number of records matching query criteria |
data.list | array | Array of clearing records |
Clearing Record Fields
| Field | Type | Description |
|---|---|---|
transaction_id | string | Unique ID of the transaction |
authorization_id | string | Related authorization ID (may be null for older records) |
card_id | string | Unique ID of the card |
card_number | string | Masked card number (e.g., 5157****8888) |
last4 | string | Last 4 digits of card number |
remark | string | Card remark/label |
posting_date | string | Date when transaction settled |
transaction_date | string | Date when transaction was processed |
merchant_name | string | Name of merchant |
merchant_country | string | Country of merchant |
mcc | string | Merchant category code |
type | string | Transaction type: Credit or Debit |
billing_amount | string | Amount in billing currency |
billing_currency | string | Billing currency code |
transaction_amount | string | Amount in merchant's currency |
transaction_currency | string | Merchant's currency code |
approval_code | string | Approval code from card network |
budget_id | string | Budget account ID |
cross_border_fee | string | Fee for cross-border transactions |
cross_border_fee_currency | string | Currency of cross-border fee |
Use Cases
Use Case 1: Monthly Financial Reconciliation
Reconcile all cleared transactions for month-end financial reporting and accounting.
curl -X GET "https://api.ahrvo.network/card/issuance/api/issuing/transaction/v2/clearings?start_posting_date=2026-01-01%2000:00:00&end_posting_date=2026-01-31%2023:59:59&page_size=100" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "x-api-key: YOUR_API_KEY"
Python Implementation:
import requests
from datetime import datetime, timedelta
from collections import defaultdict
import csv
class ClearingReconciliation:
def __init__(self, api_key, access_token):
self.api_key = api_key
self.access_token = access_token
self.base_url = "https://api.ahrvo.network/card/issuance"
def get_monthly_clearings(self, year, month):
"""Get all cleared transactions for a specific month"""
# Calculate month date range
start_date = datetime(year, month, 1)
if month == 12:
end_date = datetime(year + 1, 1, 1) - timedelta(seconds=1)
else:
end_date = datetime(year, month + 1, 1) - timedelta(seconds=1)
headers = {
"Authorization": f"Bearer {self.access_token}",
"x-api-key": self.api_key
}
params = {
"start_posting_date": start_date.strftime("%Y-%m-%d %H:%M:%S"),
"end_posting_date": end_date.strftime("%Y-%m-%d %H:%M:%S"),
"page_size": 100,
"page_no": 1
}
all_clearings = []
# Fetch all pages
while True:
response = requests.get(
f"{self.base_url}/api/issuing/transaction/v2/clearings",
headers=headers,
params=params
)
response.raise_for_status()
data = response.json()
if data["code"] == "SUCCESS":
clearings = data["data"]["list"]
all_clearings.extend(clearings)
total_num = data["data"]["total_num"]
current_records = params["page_no"] * params["page_size"]
if current_records >= total_num:
break
params["page_no"] += 1
else:
raise Exception(f"Failed to fetch clearings: {data}")
return all_clearings
def reconcile_transactions(self, year, month):
"""Reconcile transactions and generate financial report"""
clearings = self.get_monthly_clearings(year, month)
# Separate debits and credits
debits = [c for c in clearings if c["type"] == "Debit"]
credits = [c for c in clearings if c["type"] == "Credit"]
# Calculate totals
total_debit = sum(float(c["billing_amount"]) for c in debits)
total_credit = sum(float(c["billing_amount"]) for c in credits)
net_spending = total_debit - total_credit
# Calculate cross-border fees
cross_border_fees = sum(
float(c.get("cross_border_fee", 0))
for c in clearings
if c.get("cross_border_fee")
)
# Group by card
by_card = defaultdict(lambda: {
"debits": 0.0,
"credits": 0.0,
"net": 0.0,
"count": 0,
"card_number": "",
"remark": ""
})
for clearing in clearings:
card_id = clearing["card_id"]
amount = float(clearing["billing_amount"])
by_card[card_id]["count"] += 1
by_card[card_id]["card_number"] = clearing["card_number"]
by_card[card_id]["remark"] = clearing.get("remark", "")
if clearing["type"] == "Debit":
by_card[card_id]["debits"] += amount
else:
by_card[card_id]["credits"] += amount
by_card[card_id]["net"] = (
by_card[card_id]["debits"] - by_card[card_id]["credits"]
)
# Group by budget
by_budget = defaultdict(lambda: {
"debits": 0.0,
"credits": 0.0,
"net": 0.0,
"count": 0
})
for clearing in clearings:
budget_id = clearing.get("budget_id")
if budget_id:
amount = float(clearing["billing_amount"])
by_budget[budget_id]["count"] += 1
if clearing["type"] == "Debit":
by_budget[budget_id]["debits"] += amount
else:
by_budget[budget_id]["credits"] += amount
by_budget[budget_id]["net"] = (
by_budget[budget_id]["debits"] - by_budget[budget_id]["credits"]
)
# Group by merchant category
by_category = defaultdict(lambda: {"count": 0, "amount": 0.0})
for clearing in debits: # Only count spending (debits)
mcc = clearing.get("mcc", "Unknown")
by_category[mcc]["count"] += 1
by_category[mcc]["amount"] += float(clearing["billing_amount"])
return {
"period": f"{year}-{month:02d}",
"summary": {
"total_transactions": len(clearings),
"total_debits": total_debit,
"total_credits": total_credit,
"net_spending": net_spending,
"cross_border_fees": cross_border_fees,
"debit_count": len(debits),
"credit_count": len(credits)
},
"by_card": dict(by_card),
"by_budget": dict(by_budget),
"by_category": dict(by_category),
"transactions": clearings
}
def export_to_csv(self, reconciliation, filename):
"""Export reconciliation report to CSV"""
with open(filename, 'w', newline='') as csvfile:
writer = csv.writer(csvfile)
# Header
writer.writerow(["MONTHLY RECONCILIATION REPORT"])
writer.writerow(["Period", reconciliation["period"]])
writer.writerow([])
# Summary
writer.writerow(["SUMMARY"])
summary = reconciliation["summary"]
writer.writerow(["Total Transactions", summary["total_transactions"]])
writer.writerow(["Total Debits", f"${summary['total_debits']:.2f}"])
writer.writerow(["Total Credits", f"${summary['total_credits']:.2f}"])
writer.writerow(["Net Spending", f"${summary['net_spending']:.2f}"])
writer.writerow(["Cross-Border Fees", f"${summary['cross_border_fees']:.2f}"])
writer.writerow([])
# By Card
writer.writerow(["BY CARD"])
writer.writerow([
"Card ID", "Card Number", "Remark", "Debits",
"Credits", "Net", "Transactions"
])
for card_id, data in reconciliation["by_card"].items():
writer.writerow([
card_id,
data["card_number"],
data["remark"],
f"${data['debits']:.2f}",
f"${data['credits']:.2f}",
f"${data['net']:.2f}",
data["count"]
])
writer.writerow([])
# By Budget
if reconciliation["by_budget"]:
writer.writerow(["BY BUDGET"])
writer.writerow(["Budget ID", "Debits", "Credits", "Net", "Transactions"])
for budget_id, data in reconciliation["by_budget"].items():
writer.writerow([
budget_id,
f"${data['debits']:.2f}",
f"${data['credits']:.2f}",
f"${data['net']:.2f}",
data["count"]
])
writer.writerow([])
# All Transactions
writer.writerow(["ALL TRANSACTIONS"])
writer.writerow([
"Transaction ID", "Posting Date", "Transaction Date",
"Card Number", "Merchant", "Type", "Amount", "Currency"
])
for txn in reconciliation["transactions"]:
writer.writerow([
txn["transaction_id"],
txn["posting_date"],
txn["transaction_date"],
txn["card_number"],
txn.get("merchant_name", ""),
txn["type"],
txn["billing_amount"],
txn["billing_currency"]
])
print(f"Reconciliation report exported to {filename}")
def print_reconciliation_report(self, reconciliation):
"""Print formatted reconciliation report"""
print("\n" + "="*70)
print(f"MONTHLY RECONCILIATION REPORT - {reconciliation['period']}")
print("="*70)
summary = reconciliation["summary"]
print(f"\nSUMMARY:")
print(f" Total Transactions: {summary['total_transactions']}")
print(f" Total Debits: ${summary['total_debits']:.2f} ({summary['debit_count']} txns)")
print(f" Total Credits: ${summary['total_credits']:.2f} ({summary['credit_count']} txns)")
print(f" Net Spending: ${summary['net_spending']:.2f}")
print(f" Cross-Border Fees: ${summary['cross_border_fees']:.2f}")
print(f"\nBY CARD:")
sorted_cards = sorted(
reconciliation["by_card"].items(),
key=lambda x: x[1]["net"],
reverse=True
)
for card_id, data in sorted_cards:
print(f"\n {data['card_number']} ({data['remark']})")
print(f" Debits: ${data['debits']:.2f}")
print(f" Credits: ${data['credits']:.2f}")
print(f" Net Spending: ${data['net']:.2f}")
print(f" Transactions: {data['count']}")
print(f"\nTOP SPENDING CATEGORIES:")
sorted_categories = sorted(
reconciliation["by_category"].items(),
key=lambda x: x[1]["amount"],
reverse=True
)[:5]
for mcc, data in sorted_categories:
print(f" MCC {mcc}: ${data['amount']:.2f} ({data['count']} txns)")
# Usage
reconciler = ClearingReconciliation("your-api-key", "your-access-token")
reconciliation = reconciler.reconcile_transactions(2026, 1)
reconciler.print_reconciliation_report(reconciliation)
reconciler.export_to_csv(reconciliation, "january_2026_reconciliation.csv")
JavaScript Implementation:
class ClearingReconciliation {
constructor(apiKey, accessToken) {
this.apiKey = apiKey;
this.accessToken = accessToken;
this.baseUrl = 'https://api.ahrvo.network/card/issuance';
}
async getMonthlyClearings(year, month) {
const startDate = new Date(year, month - 1, 1);
const endDate = new Date(year, month, 0, 23, 59, 59);
const headers = {
'Authorization': `Bearer ${this.accessToken}`,
'x-api-key': this.apiKey
};
let allClearings = [];
let pageNo = 1;
const pageSize = 100;
while (true) {
const params = new URLSearchParams({
start_posting_date: this.formatDate(startDate),
end_posting_date: this.formatDate(endDate),
page_size: pageSize,
page_no: pageNo
});
const response = await fetch(
`${this.baseUrl}/api/issuing/transaction/v2/clearings?${params}`,
{ headers }
);
const data = await response.json();
if (data.code === 'SUCCESS') {
allClearings.push(...data.data.list);
const totalNum = data.data.total_num;
const currentRecords = pageNo * pageSize;
if (currentRecords >= totalNum) break;
pageNo++;
} else {
throw new Error(`Failed to fetch clearings: ${JSON.stringify(data)}`);
}
}
return allClearings;
}
formatDate(date) {
return date.toISOString().slice(0, 19).replace('T', ' ');
}
async reconcileTransactions(year, month) {
const clearings = await this.getMonthlyClearings(year, month);
const debits = clearings.filter(c => c.type === 'Debit');
const credits = clearings.filter(c => c.type === 'Credit');
const totalDebit = debits.reduce((sum, c) => sum + parseFloat(c.billing_amount), 0);
const totalCredit = credits.reduce((sum, c) => sum + parseFloat(c.billing_amount), 0);
const netSpending = totalDebit - totalCredit;
const crossBorderFees = clearings.reduce((sum, c) =>
sum + parseFloat(c.cross_border_fee || 0), 0
);
// Group by card
const byCard = {};
clearings.forEach(clearing => {
const cardId = clearing.card_id;
if (!byCard[cardId]) {
byCard[cardId] = {
debits: 0,
credits: 0,
net: 0,
count: 0,
card_number: clearing.card_number,
remark: clearing.remark || ''
};
}
byCard[cardId].count++;
const amount = parseFloat(clearing.billing_amount);
if (clearing.type === 'Debit') {
byCard[cardId].debits += amount;
} else {
byCard[cardId].credits += amount;
}
byCard[cardId].net = byCard[cardId].debits - byCard[cardId].credits;
});
return {
period: `${year}-${month.toString().padStart(2, '0')}`,
summary: {
total_transactions: clearings.length,
total_debits: totalDebit,
total_credits: totalCredit,
net_spending: netSpending,
cross_border_fees: crossBorderFees,
debit_count: debits.length,
credit_count: credits.length
},
by_card: byCard,
transactions: clearings
};
}
printReconciliationReport(reconciliation) {
console.log('\n' + '='.repeat(70));
console.log(`MONTHLY RECONCILIATION REPORT - ${reconciliation.period}`);
console.log('='.repeat(70));
const summary = reconciliation.summary;
console.log('\nSUMMARY:');
console.log(` Total Transactions: ${summary.total_transactions}`);
console.log(` Total Debits: $${summary.total_debits.toFixed(2)} (${summary.debit_count} txns)`);
console.log(` Total Credits: $${summary.total_credits.toFixed(2)} (${summary.credit_count} txns)`);
console.log(` Net Spending: $${summary.net_spending.toFixed(2)}`);
console.log(` Cross-Border Fees: $${summary.cross_border_fees.toFixed(2)}`);
console.log('\nBY CARD:');
Object.entries(reconciliation.by_card)
.sort((a, b) => b[1].net - a[1].net)
.forEach(([cardId, data]) => {
console.log(`\n ${data.card_number} (${data.remark})`);
console.log(` Debits: $${data.debits.toFixed(2)}`);
console.log(` Credits: $${data.credits.toFixed(2)}`);
console.log(` Net Spending: $${data.net.toFixed(2)}`);
console.log(` Transactions: ${data.count}`);
});
}
}
// Usage
const reconciler = new ClearingReconciliation('your-api-key', 'your-access-token');
const reconciliation = await reconciler.reconcileTransactions(2026, 1);
reconciler.printReconciliationReport(reconciliation);
Topics Covered:
- Monthly date range calculation
- Debit/credit separation
- Net spending calculation
- Cross-border fee aggregation
- Card-level reconciliation
- Budget-level grouping
- CSV export for accounting
- Multi-currency handling
Use Case 2: Authorization to Clearing Reconciliation
Match authorizations with their corresponding clearings to identify discrepancies and track settlement timing.
Python Implementation:
import requests
from datetime import datetime, timedelta
from collections import defaultdict
class AuthorizationClearingMatcher:
def __init__(self, api_key, access_token):
self.api_key = api_key
self.access_token = access_token
self.base_url = "https://api.ahrvo.network/card/issuance"
def get_authorizations(self, start_date, end_date):
"""Fetch all authorizations in date range"""
headers = {
"Authorization": f"Bearer {self.access_token}",
"x-api-key": self.api_key
}
params = {
"start_date": start_date,
"end_date": end_date,
"page_size": 100,
"page_no": 1
}
all_authorizations = []
while True:
response = requests.get(
f"{self.base_url}/api/issuing/transaction/v2/authorizations",
headers=headers,
params=params
)
response.raise_for_status()
data = response.json()
if data["code"] == "SUCCESS":
all_authorizations.extend(data["data"]["list"])
total_num = data["data"]["total_num"]
if params["page_no"] * params["page_size"] >= total_num:
break
params["page_no"] += 1
else:
break
return all_authorizations
def get_clearings(self, start_date, end_date):
"""Fetch all clearings in date range"""
headers = {
"Authorization": f"Bearer {self.access_token}",
"x-api-key": self.api_key
}
params = {
"start_posting_date": start_date,
"end_posting_date": end_date,
"page_size": 100,
"page_no": 1
}
all_clearings = []
while True:
response = requests.get(
f"{self.base_url}/api/issuing/transaction/v2/clearings",
headers=headers,
params=params
)
response.raise_for_status()
data = response.json()
if data["code"] == "SUCCESS":
all_clearings.extend(data["data"]["list"])
total_num = data["data"]["total_num"]
if params["page_no"] * params["page_size"] >= total_num:
break
params["page_no"] += 1
else:
break
return all_clearings
def match_auth_to_clearing(self, start_date, end_date):
"""Match authorizations with their clearings"""
# Get authorizations
authorizations = self.get_authorizations(start_date, end_date)
# Get clearings (extend range to catch late settlements)
clearing_end = (
datetime.strptime(end_date, "%Y-%m-%d %H:%M:%S") +
timedelta(days=7)
).strftime("%Y-%m-%d %H:%M:%S")
clearings = self.get_clearings(start_date, clearing_end)
# Build authorization lookup
auth_lookup = {}
for auth in authorizations:
if auth["authorization_status"] == "APPROVED":
auth_lookup[auth["authorization_id"]] = auth
# Match clearings to authorizations
matched = []
unmatched_clearings = []
for clearing in clearings:
auth_id = clearing.get("authorization_id")
if auth_id and auth_id in auth_lookup:
matched.append({
"authorization": auth_lookup[auth_id],
"clearing": clearing,
"auth_amount": float(auth_lookup[auth_id]["billing_amount"]),
"clearing_amount": float(clearing["billing_amount"]),
"amount_match": (
float(auth_lookup[auth_id]["billing_amount"]) ==
float(clearing["billing_amount"])
),
"settlement_time": self._calculate_settlement_time(
auth_lookup[auth_id]["authorization_date"],
clearing["posting_date"]
)
})
# Remove from lookup to track uncleared authorizations
del auth_lookup[auth_id]
else:
unmatched_clearings.append(clearing)
# Remaining authorizations are uncleared
uncleared_authorizations = list(auth_lookup.values())
# Calculate statistics
total_matched = len(matched)
amount_mismatches = sum(1 for m in matched if not m["amount_match"])
avg_settlement_time = (
sum(m["settlement_time"] for m in matched) / total_matched
if total_matched > 0 else 0
)
return {
"matched": matched,
"unmatched_clearings": unmatched_clearings,
"uncleared_authorizations": uncleared_authorizations,
"statistics": {
"total_authorizations": len(authorizations),
"total_clearings": len(clearings),
"matched_count": total_matched,
"unmatched_clearing_count": len(unmatched_clearings),
"uncleared_auth_count": len(uncleared_authorizations),
"amount_mismatch_count": amount_mismatches,
"avg_settlement_time_hours": avg_settlement_time
}
}
def _calculate_settlement_time(self, auth_date, posting_date):
"""Calculate hours between authorization and settlement"""
auth_dt = datetime.strptime(auth_date, "%Y-%m-%d %H:%M:%S")
posting_dt = datetime.strptime(posting_date, "%Y-%m-%d")
return (posting_dt - auth_dt).total_seconds() / 3600
def print_matching_report(self, results):
"""Print formatted matching report"""
print("\n" + "="*70)
print("AUTHORIZATION-CLEARING MATCHING REPORT")
print("="*70)
stats = results["statistics"]
print(f"\nSTATISTICS:")
print(f" Total Authorizations: {stats['total_authorizations']}")
print(f" Total Clearings: {stats['total_clearings']}")
print(f" Matched: {stats['matched_count']}")
print(f" Unmatched Clearings: {stats['unmatched_clearing_count']}")
print(f" Uncleared Authorizations: {stats['uncleared_auth_count']}")
print(f" Amount Mismatches: {stats['amount_mismatch_count']}")
print(f" Avg Settlement Time: {stats['avg_settlement_time_hours']:.1f} hours")
if results["uncleared_authorizations"]:
print(f"\n⚠️ UNCLEARED AUTHORIZATIONS ({len(results['uncleared_authorizations'])}):")
for auth in results["uncleared_authorizations"][:10]:
print(f" {auth['authorization_id']} | {auth['card_number']} | "
f"${auth['billing_amount']} | {auth['authorization_date']} | "
f"{auth.get('merchant_name', 'Unknown')}")
if results["unmatched_clearings"]:
print(f"\n⚠️ UNMATCHED CLEARINGS ({len(results['unmatched_clearings'])}):")
for clearing in results["unmatched_clearings"][:10]:
print(f" {clearing['transaction_id']} | {clearing['card_number']} | "
f"${clearing['billing_amount']} | {clearing['posting_date']} | "
f"{clearing.get('merchant_name', 'Unknown')}")
# Amount mismatches
amount_mismatches = [m for m in results["matched"] if not m["amount_match"]]
if amount_mismatches:
print(f"\n⚠️ AMOUNT MISMATCHES ({len(amount_mismatches)}):")
for mismatch in amount_mismatches[:10]:
auth = mismatch["authorization"]
clearing = mismatch["clearing"]
diff = mismatch["clearing_amount"] - mismatch["auth_amount"]
print(f" Card: {auth['card_number']}")
print(f" Auth ID: {auth['authorization_id']}")
print(f" Auth Amount: ${mismatch['auth_amount']:.2f}")
print(f" Clearing Amount: ${mismatch['clearing_amount']:.2f}")
print(f" Difference: ${diff:.2f}")
print(f" Merchant: {auth.get('merchant_name', 'Unknown')}")
print()
# Usage
matcher = AuthorizationClearingMatcher("your-api-key", "your-access-token")
results = matcher.match_auth_to_clearing(
start_date="2026-01-01 00:00:00",
end_date="2026-01-31 23:59:59"
)
matcher.print_matching_report(results)
Topics Covered:
- Authorization-to-clearing matching
- Settlement time calculation
- Amount discrepancy detection
- Uncleared authorization tracking
- Orphaned clearing identification
- Reconciliation statistics
Use Case 3: Credit Transaction Monitoring (Refunds and Returns)
Track all credit transactions to monitor refunds, returns, and chargebacks.
Python Implementation:
import requests
from datetime import datetime
from collections import defaultdict
class CreditTransactionMonitor:
def __init__(self, api_key, access_token):
self.api_key = api_key
self.access_token = access_token
self.base_url = "https://api.ahrvo.network/card/issuance"
def get_credit_transactions(self, start_date, end_date):
"""Get all credit transactions (refunds/returns)"""
headers = {
"Authorization": f"Bearer {self.access_token}",
"x-api-key": self.api_key
}
params = {
"start_posting_date": start_date,
"end_posting_date": end_date,
"type": "Credit", # Filter for credits only
"page_size": 100,
"page_no": 1
}
all_credits = []
while True:
response = requests.get(
f"{self.base_url}/api/issuing/transaction/v2/clearings",
headers=headers,
params=params
)
response.raise_for_status()
data = response.json()
if data["code"] == "SUCCESS":
all_credits.extend(data["data"]["list"])
total_num = data["data"]["total_num"]
if params["page_no"] * params["page_size"] >= total_num:
break
params["page_no"] += 1
else:
break
return all_credits
def analyze_credits(self, start_date, end_date):
"""Analyze credit transaction patterns"""
credits = self.get_credit_transactions(start_date, end_date)
# Total credit amount
total_credit_amount = sum(float(c["billing_amount"]) for c in credits)
# Group by card
by_card = defaultdict(lambda: {
"count": 0,
"total_amount": 0.0,
"card_number": "",
"remark": "",
"transactions": []
})
for credit in credits:
card_id = credit["card_id"]
by_card[card_id]["count"] += 1
by_card[card_id]["total_amount"] += float(credit["billing_amount"])
by_card[card_id]["card_number"] = credit["card_number"]
by_card[card_id]["remark"] = credit.get("remark", "")
by_card[card_id]["transactions"].append(credit)
# Group by merchant
by_merchant = defaultdict(lambda: {
"count": 0,
"total_amount": 0.0,
"transactions": []
})
for credit in credits:
merchant = credit.get("merchant_name", "Unknown")
by_merchant[merchant]["count"] += 1
by_merchant[merchant]["total_amount"] += float(credit["billing_amount"])
by_merchant[merchant]["transactions"].append(credit)
# Identify large refunds (>$1000)
large_refunds = [
c for c in credits
if float(c["billing_amount"]) > 1000
]
# Calculate average refund
avg_refund = total_credit_amount / len(credits) if credits else 0
return {
"total_credits": len(credits),
"total_amount": total_credit_amount,
"average_refund": avg_refund,
"by_card": dict(by_card),
"by_merchant": dict(by_merchant),
"large_refunds": large_refunds,
"all_credits": credits
}
def print_credit_report(self, analysis):
"""Print formatted credit transaction report"""
print("\n" + "="*70)
print("CREDIT TRANSACTION REPORT (Refunds & Returns)")
print("="*70)
print(f"\nOVERVIEW:")
print(f" Total Credit Transactions: {analysis['total_credits']}")
print(f" Total Credit Amount: ${analysis['total_amount']:.2f}")
print(f" Average Refund: ${analysis['average_refund']:.2f}")
print(f"\nTOP CARDS BY CREDIT AMOUNT:")
sorted_cards = sorted(
analysis["by_card"].items(),
key=lambda x: x[1]["total_amount"],
reverse=True
)[:5]
for card_id, data in sorted_cards:
print(f"\n {data['card_number']} ({data['remark']})")
print(f" Credits: {data['count']}")
print(f" Total Amount: ${data['total_amount']:.2f}")
print(f"\nTOP MERCHANTS BY REFUND COUNT:")
sorted_merchants = sorted(
analysis["by_merchant"].items(),
key=lambda x: x[1]["count"],
reverse=True
)[:5]
for merchant, data in sorted_merchants:
print(f" {merchant}: {data['count']} refunds, ${data['total_amount']:.2f}")
if analysis["large_refunds"]:
print(f"\nLARGE REFUNDS (>$1000) - {len(analysis['large_refunds'])} transactions:")
for refund in analysis["large_refunds"]:
print(f" ${refund['billing_amount']} | {refund['card_number']} | "
f"{refund.get('merchant_name', 'Unknown')} | {refund['posting_date']}")
# Usage
monitor = CreditTransactionMonitor("your-api-key", "your-access-token")
analysis = monitor.analyze_credits(
start_date="2026-01-01 00:00:00",
end_date="2026-01-31 23:59:59"
)
monitor.print_credit_report(analysis)
Topics Covered:
- Credit transaction filtering
- Refund amount aggregation
- Card-level credit tracking
- Merchant refund patterns
- Large refund identification
- Return rate analysis
Use Case 4: Cross-Border Transaction Analysis
Analyze international transactions and associated cross-border fees.
Python Implementation:
import requests
from collections import defaultdict
class CrossBorderAnalyzer:
def __init__(self, api_key, access_token):
self.api_key = api_key
self.access_token = access_token
self.base_url = "https://api.ahrvo.network/card/issuance"
def get_clearings(self, start_date, end_date):
"""Get all clearings in date range"""
headers = {
"Authorization": f"Bearer {self.access_token}",
"x-api-key": self.api_key
}
params = {
"start_posting_date": start_date,
"end_posting_date": end_date,
"page_size": 100,
"page_no": 1
}
all_clearings = []
while True:
response = requests.get(
f"{self.base_url}/api/issuing/transaction/v2/clearings",
headers=headers,
params=params
)
response.raise_for_status()
data = response.json()
if data["code"] == "SUCCESS":
all_clearings.extend(data["data"]["list"])
total_num = data["data"]["total_num"]
if params["page_no"] * params["page_size"] >= total_num:
break
params["page_no"] += 1
else:
break
return all_clearings
def analyze_cross_border(self, start_date, end_date):
"""Analyze cross-border transactions and fees"""
clearings = self.get_clearings(start_date, end_date)
# Identify cross-border transactions
cross_border = []
domestic = []
for clearing in clearings:
fee = float(clearing.get("cross_border_fee", 0))
# Assume transaction is cross-border if it has a fee or currency mismatch
is_cross_border = (
fee > 0 or
clearing["transaction_currency"] != clearing["billing_currency"]
)
if is_cross_border:
cross_border.append(clearing)
else:
domestic.append(clearing)
# Calculate totals
total_cross_border_volume = sum(
float(c["billing_amount"]) for c in cross_border
)
total_cross_border_fees = sum(
float(c.get("cross_border_fee", 0)) for c in cross_border
)
# Group by country
by_country = defaultdict(lambda: {
"count": 0,
"volume": 0.0,
"fees": 0.0,
"avg_fee": 0.0
})
for txn in cross_border:
country = txn.get("merchant_country", "Unknown")
by_country[country]["count"] += 1
by_country[country]["volume"] += float(txn["billing_amount"])
by_country[country]["fees"] += float(txn.get("cross_border_fee", 0))
# Calculate average fees
for country, data in by_country.items():
if data["count"] > 0:
data["avg_fee"] = data["fees"] / data["count"]
# Group by currency pair
by_currency_pair = defaultdict(lambda: {
"count": 0,
"volume": 0.0,
"fees": 0.0
})
for txn in cross_border:
pair = f"{txn['transaction_currency']} → {txn['billing_currency']}"
by_currency_pair[pair]["count"] += 1
by_currency_pair[pair]["volume"] += float(txn["billing_amount"])
by_currency_pair[pair]["fees"] += float(txn.get("cross_border_fee", 0))
# Group by card
by_card = defaultdict(lambda: {
"count": 0,
"volume": 0.0,
"fees": 0.0,
"card_number": "",
"remark": ""
})
for txn in cross_border:
card_id = txn["card_id"]
by_card[card_id]["count"] += 1
by_card[card_id]["volume"] += float(txn["billing_amount"])
by_card[card_id]["fees"] += float(txn.get("cross_border_fee", 0))
by_card[card_id]["card_number"] = txn["card_number"]
by_card[card_id]["remark"] = txn.get("remark", "")
# Calculate percentage
total_volume = sum(float(c["billing_amount"]) for c in clearings)
cross_border_percentage = (
(total_cross_border_volume / total_volume * 100)
if total_volume > 0 else 0
)
return {
"total_transactions": len(clearings),
"cross_border_count": len(cross_border),
"domestic_count": len(domestic),
"cross_border_volume": total_cross_border_volume,
"cross_border_fees": total_cross_border_fees,
"cross_border_percentage": cross_border_percentage,
"by_country": dict(by_country),
"by_currency_pair": dict(by_currency_pair),
"by_card": dict(by_card),
"transactions": cross_border
}
def print_cross_border_report(self, analysis):
"""Print formatted cross-border analysis report"""
print("\n" + "="*70)
print("CROSS-BORDER TRANSACTION ANALYSIS")
print("="*70)
print(f"\nOVERVIEW:")
print(f" Total Transactions: {analysis['total_transactions']}")
print(f" Cross-Border: {analysis['cross_border_count']} "
f"({analysis['cross_border_percentage']:.1f}%)")
print(f" Domestic: {analysis['domestic_count']}")
print(f" Cross-Border Volume: ${analysis['cross_border_volume']:.2f}")
print(f" Total Cross-Border Fees: ${analysis['cross_border_fees']:.2f}")
print(f"\nBY COUNTRY:")
sorted_countries = sorted(
analysis["by_country"].items(),
key=lambda x: x[1]["volume"],
reverse=True
)[:10]
for country, data in sorted_countries:
print(f"\n {country}")
print(f" Transactions: {data['count']}")
print(f" Volume: ${data['volume']:.2f}")
print(f" Fees: ${data['fees']:.2f}")
print(f" Avg Fee: ${data['avg_fee']:.2f}")
print(f"\nBY CURRENCY CONVERSION:")
sorted_pairs = sorted(
analysis["by_currency_pair"].items(),
key=lambda x: x[1]["count"],
reverse=True
)[:5]
for pair, data in sorted_pairs:
print(f" {pair}: {data['count']} txns, ${data['volume']:.2f}, "
f"fees: ${data['fees']:.2f}")
print(f"\nTOP CARDS BY CROSS-BORDER FEES:")
sorted_cards = sorted(
analysis["by_card"].items(),
key=lambda x: x[1]["fees"],
reverse=True
)[:5]
for card_id, data in sorted_cards:
print(f"\n {data['card_number']} ({data['remark']})")
print(f" Transactions: {data['count']}")
print(f" Volume: ${data['volume']:.2f}")
print(f" Fees: ${data['fees']:.2f}")
# Usage
analyzer = CrossBorderAnalyzer("your-api-key", "your-access-token")
analysis = analyzer.analyze_cross_border(
start_date="2026-01-01 00:00:00",
end_date="2026-01-31 23:59:59"
)
analyzer.print_cross_border_report(analysis)
Topics Covered:
- Cross-border transaction identification
- International fee calculation
- Country-based analysis
- Currency conversion tracking
- Fee optimization opportunities
- Geographic spending patterns
Use Case 5: Expense Categorization by MCC
Categorize and analyze spending by merchant category codes for expense reporting.
Python Implementation:
import requests
from collections import defaultdict
# MCC Category Mappings
MCC_CATEGORIES = {
"Airlines": ["3000-3299", "4511"],
"Hotels": ["3500-3999", "7011"],
"Restaurants": ["5812", "5813", "5814"],
"Gas Stations": ["5541", "5542"],
"Grocery": ["5411", "5422", "5499"],
"Transportation": ["4111", "4121", "4131", "4789"],
"Entertainment": ["7832", "7841", "7922", "7929", "7991", "7996"],
"Online Services": ["5816", "5817", "5818"],
"Office Supplies": ["5111", "5943", "5044"],
"Professional Services": ["8111", "8931"]
}
class ExpenseCategorizer:
def __init__(self, api_key, access_token):
self.api_key = api_key
self.access_token = access_token
self.base_url = "https://api.ahrvo.network/card/issuance"
def get_clearings(self, start_date, end_date, card_id=None):
"""Get clearing transactions for categorization"""
headers = {
"Authorization": f"Bearer {self.access_token}",
"x-api-key": self.api_key
}
params = {
"start_posting_date": start_date,
"end_posting_date": end_date,
"type": "Debit", # Only expenses
"page_size": 100,
"page_no": 1
}
if card_id:
params["card_id"] = card_id
all_clearings = []
while True:
response = requests.get(
f"{self.base_url}/api/issuing/transaction/v2/clearings",
headers=headers,
params=params
)
response.raise_for_status()
data = response.json()
if data["code"] == "SUCCESS":
all_clearings.extend(data["data"]["list"])
total_num = data["data"]["total_num"]
if params["page_no"] * params["page_size"] >= total_num:
break
params["page_no"] += 1
else:
break
return all_clearings
def categorize_by_mcc(self, start_date, end_date, card_id=None):
"""Categorize expenses by merchant category"""
clearings = self.get_clearings(start_date, end_date, card_id)
# Initialize categories
categorized = defaultdict(lambda: {
"count": 0,
"amount": 0.0,
"transactions": []
})
# Categorize each transaction
for clearing in clearings:
mcc = clearing.get("mcc", "")
amount = float(clearing["billing_amount"])
category = self._get_category_from_mcc(mcc)
categorized[category]["count"] += 1
categorized[category]["amount"] += amount
categorized[category]["transactions"].append(clearing)
# Calculate totals
total_amount = sum(cat["amount"] for cat in categorized.values())
# Add percentages
for category, data in categorized.items():
data["percentage"] = (
(data["amount"] / total_amount * 100)
if total_amount > 0 else 0
)
return {
"total_transactions": len(clearings),
"total_amount": total_amount,
"categories": dict(categorized),
"period": f"{start_date} to {end_date}"
}
def _get_category_from_mcc(self, mcc):
"""Map MCC to category"""
if not mcc:
return "Uncategorized"
for category, mcc_list in MCC_CATEGORIES.items():
if mcc in mcc_list:
return category
# Check range (e.g., "3000-3299")
for mcc_range in mcc_list:
if "-" in mcc_range:
start, end = mcc_range.split("-")
if start <= mcc <= end:
return category
return "Other"
def export_expense_report(self, analysis, filename):
"""Export categorized expenses to CSV"""
import csv
with open(filename, 'w', newline='') as csvfile:
writer = csv.writer(csvfile)
# Header
writer.writerow(["EXPENSE REPORT BY CATEGORY"])
writer.writerow(["Period", analysis["period"]])
writer.writerow(["Total Amount", f"${analysis['total_amount']:.2f}"])
writer.writerow([])
# Summary by category
writer.writerow(["CATEGORY SUMMARY"])
writer.writerow(["Category", "Transactions", "Amount", "Percentage"])
sorted_categories = sorted(
analysis["categories"].items(),
key=lambda x: x[1]["amount"],
reverse=True
)
for category, data in sorted_categories:
writer.writerow([
category,
data["count"],
f"${data['amount']:.2f}",
f"{data['percentage']:.1f}%"
])
writer.writerow([])
# Detailed transactions
writer.writerow(["DETAILED TRANSACTIONS"])
writer.writerow([
"Category", "Date", "Merchant", "Card",
"Amount", "Currency", "MCC"
])
for category, data in sorted_categories:
for txn in data["transactions"]:
writer.writerow([
category,
txn["posting_date"],
txn.get("merchant_name", ""),
txn["card_number"],
txn["billing_amount"],
txn["billing_currency"],
txn.get("mcc", "")
])
print(f"Expense report exported to {filename}")
def print_expense_report(self, analysis):
"""Print formatted expense report"""
print("\n" + "="*70)
print("EXPENSE CATEGORIZATION REPORT")
print("="*70)
print(f"\nPeriod: {analysis['period']}")
print(f"Total Transactions: {analysis['total_transactions']}")
print(f"Total Amount: ${analysis['total_amount']:.2f}")
print(f"\nBY CATEGORY:")
sorted_categories = sorted(
analysis["categories"].items(),
key=lambda x: x[1]["amount"],
reverse=True
)
for category, data in sorted_categories:
print(f"\n {category}")
print(f" Transactions: {data['count']}")
print(f" Amount: ${data['amount']:.2f} ({data['percentage']:.1f}%)")
# Top merchants in category
merchant_totals = defaultdict(float)
for txn in data["transactions"]:
merchant = txn.get("merchant_name", "Unknown")
merchant_totals[merchant] += float(txn["billing_amount"])
top_merchants = sorted(
merchant_totals.items(),
key=lambda x: x[1],
reverse=True
)[:3]
if top_merchants:
print(f" Top Merchants:")
for merchant, amount in top_merchants:
print(f" - {merchant}: ${amount:.2f}")
# Usage
categorizer = ExpenseCategorizer("your-api-key", "your-access-token")
analysis = categorizer.categorize_by_mcc(
start_date="2026-01-01 00:00:00",
end_date="2026-01-31 23:59:59"
)
categorizer.print_expense_report(analysis)
categorizer.export_expense_report(analysis, "january_2026_expenses.csv")
Topics Covered:
- MCC-based categorization
- Expense category grouping
- Percentage distribution
- Top merchant identification
- CSV export for accounting
- Customizable category mappings
Use Case 6: Settlement Timing Analysis
Analyze settlement patterns to understand when transactions clear and optimize cash flow.
Python Implementation:
import requests
from datetime import datetime
from collections import defaultdict
class SettlementTimingAnalyzer:
def __init__(self, api_key, access_token):
self.api_key = api_key
self.access_token = access_token
self.base_url = "https://api.ahrvo.network/card/issuance"
def get_clearings(self, start_date, end_date):
"""Get clearings with transaction and posting dates"""
headers = {
"Authorization": f"Bearer {self.access_token}",
"x-api-key": self.api_key
}
params = {
"start_posting_date": start_date,
"end_posting_date": end_date,
"page_size": 100,
"page_no": 1
}
all_clearings = []
while True:
response = requests.get(
f"{self.base_url}/api/issuing/transaction/v2/clearings",
headers=headers,
params=params
)
response.raise_for_status()
data = response.json()
if data["code"] == "SUCCESS":
all_clearings.extend(data["data"]["list"])
total_num = data["data"]["total_num"]
if params["page_no"] * params["page_size"] >= total_num:
break
params["page_no"] += 1
else:
break
return all_clearings
def analyze_settlement_timing(self, start_date, end_date):
"""Analyze time between transaction and settlement"""
clearings = self.get_clearings(start_date, end_date)
settlement_times = []
by_merchant = defaultdict(list)
by_mcc = defaultdict(list)
by_day_of_week = defaultdict(list)
for clearing in clearings:
# Calculate settlement time
txn_date = datetime.strptime(
clearing["transaction_date"],
"%Y-%m-%d"
)
posting_date = datetime.strptime(
clearing["posting_date"],
"%Y-%m-%d"
)
days_to_settle = (posting_date - txn_date).days
settlement_times.append(days_to_settle)
# Group by merchant
merchant = clearing.get("merchant_name", "Unknown")
by_merchant[merchant].append(days_to_settle)
# Group by MCC
mcc = clearing.get("mcc", "Unknown")
by_mcc[mcc].append(days_to_settle)
# Group by day of week
day_of_week = txn_date.strftime("%A")
by_day_of_week[day_of_week].append(days_to_settle)
# Calculate statistics
avg_settlement = sum(settlement_times) / len(settlement_times) if settlement_times else 0
# Distribution
settlement_distribution = defaultdict(int)
for days in settlement_times:
settlement_distribution[days] += 1
# Merchant averages
merchant_averages = {}
for merchant, times in by_merchant.items():
if len(times) >= 3: # Only include merchants with 3+ transactions
merchant_averages[merchant] = sum(times) / len(times)
# Day of week averages
dow_averages = {}
for day, times in by_day_of_week.items():
dow_averages[day] = sum(times) / len(times) if times else 0
return {
"total_transactions": len(clearings),
"average_settlement_days": avg_settlement,
"settlement_distribution": dict(settlement_distribution),
"merchant_averages": merchant_averages,
"mcc_analysis": {
mcc: {
"count": len(times),
"avg_days": sum(times) / len(times) if times else 0
}
for mcc, times in by_mcc.items()
if len(times) >= 3
},
"day_of_week_averages": dow_averages,
"fastest_settlements": [
c for c in clearings
if (datetime.strptime(c["posting_date"], "%Y-%m-%d") -
datetime.strptime(c["transaction_date"], "%Y-%m-%d")).days == 0
][:10],
"slowest_settlements": sorted(
[
(c, (datetime.strptime(c["posting_date"], "%Y-%m-%d") -
datetime.strptime(c["transaction_date"], "%Y-%m-%d")).days)
for c in clearings
],
key=lambda x: x[1],
reverse=True
)[:10]
}
def print_timing_report(self, analysis):
"""Print formatted settlement timing report"""
print("\n" + "="*70)
print("SETTLEMENT TIMING ANALYSIS")
print("="*70)
print(f"\nOVERVIEW:")
print(f" Total Transactions: {analysis['total_transactions']}")
print(f" Average Settlement Time: {analysis['average_settlement_days']:.1f} days")
print(f"\nSETTLEMENT DISTRIBUTION:")
for days, count in sorted(analysis["settlement_distribution"].items()):
percentage = (count / analysis["total_transactions"] * 100)
print(f" {days} days: {count} transactions ({percentage:.1f}%)")
print(f"\nBY DAY OF WEEK (Transaction Date):")
days_order = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
for day in days_order:
if day in analysis["day_of_week_averages"]:
print(f" {day}: {analysis['day_of_week_averages'][day]:.1f} days avg")
print(f"\nFASTEST SETTLING MERCHANTS (Same-day settlement):")
fastest = analysis["fastest_settlements"]
if fastest:
merchant_counts = defaultdict(int)
for txn in fastest:
merchant_counts[txn.get("merchant_name", "Unknown")] += 1
for merchant, count in sorted(
merchant_counts.items(),
key=lambda x: x[1],
reverse=True
)[:5]:
print(f" {merchant}: {count} same-day settlements")
print(f"\nSLOWEST SETTLEMENTS:")
for clearing, days in analysis["slowest_settlements"][:5]:
print(f" {days} days | {clearing['card_number']} | "
f"{clearing.get('merchant_name', 'Unknown')} | "
f"${clearing['billing_amount']}")
# Usage
analyzer = SettlementTimingAnalyzer("your-api-key", "your-access-token")
analysis = analyzer.analyze_settlement_timing(
start_date="2026-01-01 00:00:00",
end_date="2026-01-31 23:59:59"
)
analyzer.print_timing_report(analysis)
Topics Covered:
- Settlement time calculation
- Time-to-clear distribution
- Merchant settlement patterns
- Day-of-week analysis
- Fast/slow settlement identification
- Cash flow optimization insights
Best Practices
1. Use Posting Date for Financial Reports
posting_daterepresents when funds actually settled- Use for accurate financial reconciliation
transaction_dateshows when purchase was made- Choose appropriate date field based on use case
2. Credit vs. Debit Classification
- Filter by
typeparameter for specific transaction types - Credits include refunds, returns, chargebacks
- Debits are charges and purchases
- Important for net spending calculations
3. Reconciliation Workflows
- Match clearings to authorizations via
authorization_id - Account for settlement delays (1-3 days typical)
- Identify unmatched transactions
- Investigate amount discrepancies
4. Cross-Border Fee Tracking
- Monitor
cross_border_feefor international transactions - Compare fees across countries and merchants
- Optimize card usage for international spending
- Factor fees into total cost analysis
5. MCC-Based Categorization
- Use merchant category codes for expense classification
- Build custom category mappings for your business
- Essential for expense reporting and budgeting
- Enables policy enforcement
6. Settlement Timing Awareness
- Understand typical settlement patterns
- Plan cash flow around settlement schedules
- Identify fast-settling merchants for urgent needs
- Monitor for unusual settlement delays
Security Considerations
Authentication
- Secure storage of API keys and access tokens
- Use environment variables for credentials
- Implement token rotation
- Restrict API access to authorized systems
Data Privacy
- Clearing logs contain final transaction data
- Implement access controls for financial data
- Mask sensitive information in reports
- Comply with financial data regulations
Reconciliation Controls
- Implement dual approval for reconciliation
- Maintain audit trail of reconciliation activities
- Regular variance analysis
- Escalation procedures for discrepancies
Compliance
- Retain clearing logs per regulatory requirements
- Implement proper archival procedures
- Regular compliance audits
- Document reconciliation processes
Troubleshooting
Missing Clearings
- Issue: Authorized transactions not showing in clearings
- Solution: Allow 1-3 days for settlement, check authorization status, verify card activity
Amount Mismatches
- Issue: Clearing amount differs from authorization
- Solution: Check for tips, currency conversions, partial settlements, merchant adjustments
Duplicate Clearings
- Issue: Same transaction appears multiple times
- Solution: Check
transaction_idfor uniqueness, investigate with card network, may indicate system issue
Cross-Border Fee Errors
- Issue: Unexpected cross-border fees
- Solution: Verify merchant country, check currency differences, review card fee structure
Date Range Issues
- Issue: Missing expected transactions in date range
- Solution: Check posting_date vs. transaction_date, expand date range, verify timezone handling