This is a submission for the Xano AI-Powered Backend Challenge: Production-Ready Public API
What I Built
I built the Nigerian Government and Public Utility Fees API, a production-ready REST API that provides verified, structured, and traceable information about Nigerian government fees and public utility costs and centralizes the information from multiple Nigerian agencies into one accessible endpoint.
In Nigeria, citizens often struggle to find accurate information about the official cost of services such as:
- National Identification Number (NIN) services
- WAEC / NECO / JAMB examination fees
- Passport and identity-related processes
- Electricity tariffs across different DisCos
- Other government-regulated public fees
The problem
For most Nigerians, finding accurate government fee information is frustrating. Information is usually scattered across PDFs, outdated websites, or unofficial sources. This API centralizes that data into a clean, developer-friendly backend that can be reliably consumed by applications, chatbots, dashboards, and AI agents.
The Solution
A single API that provides:
- 90+ verified government fees across 6 categories (Identity, Immigration, Education, Electricity, Transport, Business)
- 6 official agencies: NIMC, NIS, NECO, JAMB, NERC, UNILAG
- Complete documentation with code examples in JavaScript, Python, and cURL
- Rich relationship data: Every fee includes category, agency, and official source information
- Search & filtering: Find exactly what you need instantly
The API is built with a strong focus on:
- official data sources
- traceability
- maintainability
- real-world production usage
Live API URL: https://xmlb-8xh6-ww1h.n7e.xano.io/api:public
API Documentation
The API follows a simple and predictable REST structure.
Core Resources
- Categories (e.g. Identity & Management, Education, Electricity)
- Subcategories (e.g. NIN, NECO, JAMB, Passport, DisCos)
- Fees (individual payable items)
- Agencies (government bodies)
- Sources (official documents and websites)
Base URL
https://xmlb-8xh6-ww1h.n7e.xano.io/api:public
Authentication
API key via query parameter: ?api_key=YOUR_KEY
Test API Key: nga_b6cf98a60bda43ba8cf54af9dbd87260
Available Endpoints
1. POST /api_key/generate
Generates the API key to be used for accessing the secured endpoints.
Example
curl -X 'POST''https://xmlb-8xh6-ww1h.n7e.xano.io/api:public/api_key/generate' -H 'Content-Type: application/json' --data '{"user_email":"your-email@example.com"}'
2. GET /fees
Retrieve paginated list of fees with optional filtering.
Parameters:
category (optional) - Filter by category name/slug
state (optional) - Filter by state
search (optional) - Search in name and description
page (optional, default: 1) - Page number
per_page (optional, default: 20, max: 100) - Results per page
Example:
curl 'https://xmlb-8xh6-ww1h.n7e.xano.io/api:public/fees?category=identity&per_page=5'
3. GET /fees/{id}
Get single fee by ID with all relationships.
Example:
curl 'https://xmlb-8xh6-ww1h.n7e.xano.io/api:public/fees/1?api_key=nga_b6cf98a60bda43ba8cf54af9dbd87260'
4. GET /fees/search
Search fees by name and description (min 2 characters).
Example:
curl 'https://xmlb-8xh6-ww1h.n7e.xano.io/api:public/fees/search?q=passport&api_key=nga_b6cf98a60bda43ba8cf54af9dbd87260'
5. GET /categories
Get all categories with fee counts.
Example:
curl 'https://xmlb-8xh6-ww1h.n7e.xano.io/api:public/categories?api_key=nga_b6cf98a60bda43ba8cf54af9dbd87260'
6. GET /metadata
Get API statistics and version information.
Example:
curl 'https://xmlb-8xh6-ww1h.n7e.xano.io/api:public/metadata?api_key=nga_b6cf98a60bda43ba8cf54af9dbd87260'
Rate Limits
Currently no rate limits enforced. Request tracking is implemented for future rate limiting.
Response Format
All responses are JSON. Errors follow standardized format:
{
"code": "ERROR_CODE_TYPE",
"message": "Human-readable error message"
}
Full documentation: Available in my GitHub repository with complete request/response examples, error handling, and integration guides.
Demo
Example 1: Generate API Key
Request:
curl -X 'POST''https://xmlb-8xh6-ww1h.n7e.xano.io/api:public/api_key/generate' -H 'Content-Type: application/json' --data '{"user_email":"your-email@example.com"}'
Response:
{
"success":true,
"api_key":"nga_********************************",
"message":"API key generated successfully. Please save this key as it provides access to the API."
}
Example 2: Get All Categories
Request:
curl -X 'GET' \
'https://xmlb-8xh6-ww1h.n7e.xano.io/api:public/categories?api_key=nga_b6cf98a60bda43ba8cf54af9dbd87260' \
-H 'accept: application/json'
Response:
[
{
"id": 1,
"display_name": "Identity & Management",
"description": "Fees related to national identity systems such as NIN",
"fee_count": 19
},
{
"id": 2,
"display_name": "Immigration",
"description": "Passport and visa related fees",
"fee_count": 10
},
{
"id": 3,
"display_name": "Education",
"description": "NECO, JAMB and related examination fees",
"fee_count": 45
}
]
Example 3: Search for NIN Fees
Request:
curl -X 'GET' \
'https://xmlb-8xh6-ww1h.n7e.xano.io/api:public/fees/search?q=NIN&api_key=nga_b6cf98a60bda43ba8cf54af9dbd87260' \
-H 'accept: application/json'
Response:
[
{
"id": 1,
"name": "NIN Enrolment (First Time)",
"amount": 0,
"currency": "NGN",
"service_type": "Standard",
"description": "Initial NIN enrollment is free",
"category_name": "Identity & Management",
"agency_name": "National Identity Management Commission",
"subcategory_name": "NIN",
"source_name": "NIMC"
}
]
Example 4: Get Fee Details
Request:
curl -X 'GET' \
'https://xmlb-8xh6-ww1h.n7e.xano.io/api:public/fees/20?api_key=nga_b6cf98a60bda43ba8cf54af9dbd87260' \
-H 'accept: application/json'
Response:
{
"id": 20,
"name": "Standard Passport 32 Pages (5-Year Validity)",
"amount": 100000,
"currency": "NGN",
"service_type": "Standard",
"description": "New or Renewal",
"subcategory": {
"id": 3,
"name": "Passport",
"category": {
"id": 2,
"name": "Immigration",
"description": "Passport and visa related fees"
}
},
"source": {
"id": 2,
"name": "Nigerian Immigration Service (NIS)",
"agency": {
"id": 2,
"name": "Nigeria Immigration Service",
"website": "https://immigration.gov.ng"
}
}
}
Code Integration Examples
JavaScript:
const BASE_URL = 'https://xmlb-8xh6-ww1h.n7e.xano.io/api:public';
const API_KEY = 'nga_b6cf98a60bda43ba8cf54af9dbd87260';
async function searchFees(query) {
const response = await fetch(
`${BASE_URL}/fees/search?q=${query}&api_key=${API_KEY}`
);
const data = await response.json();
console.log(`Found ${data.length} fees matching "${query}"`);
return data;
}
searchFees('passport');
Python:
import requests
BASE_URL = 'https://xmlb-8xh6-ww1h.n7e.xano.io/api:public'
API_KEY = 'nga_b6cf98a60bda43ba8cf54af9dbd87260'
def search_fees(query):
response = requests.get(
f'{BASE_URL}/fees/search',
params={'q': query, 'api_key': API_KEY}
)
data = response.json()
print(f'Found {len(data)} fees matching "{query}"')
return data
search_fees('passport')
Try it yourself with your API key, see the example provided above!
The AI Prompt I Used
I used Cursor with XanoScript to generate all 5 core endpoints. Here's my main prompt:
Generate 5 Xano API endpoints for a Nigerian government fees API:
1. GET /fees
- Query fees table with optional filters: category, state, search
- Include relationships: subcategory, category, agency, source
- Add pagination: limit (default 50), offset (default 0)
- Return JSON with data array and metadata
2. GET /fees/{id}
- Get single fee by ID with all relationships
- Return 404 if not found
- Include subcategory, category, source, and agency data
3. GET /categories
- List all categories
- Include count of fees in each category
- Return with display_name and description
4. GET /fees/search
- Accept 'q' query parameter (required, minimum 2 characters)
- Search in fee name and description using case-insensitive match
- Limit to 20 results
- Include all relationships
5. GET /metadata
- Return API statistics (total fees, categories, agencies)
- Include last database update timestamp
- Return API version number
AI delivered in ~30 minutes:
- Complete XanoScript code for all 5 endpoints
- Database query structures with joins
- Basic input validation
- Pagination logic
- Error handling with preconditions
Time saved: ~6 hours of boilerplate coding
How I Refined the AI-Generated Code
AI gave me a great starting point, but I had to fix critical bugs and add production features. Here are the key refinements:
Issue #1: Invalid eval Syntax - CRITICAL BUG

AI Generated (GET /fees/{id}):
eval = {
category : $db.{}
subcategory: $db.{}
agency : $db.{}
source : $db.{}
}
The Problem: $db.{} is invalid XanoScript syntax. This caused relationships to fail silently—no error in development, but incomplete data in production.
My Fix:
// Removed invalid eval block
// Used db.get to fetch relationships separately
var $subcategory {
value = db.get('subcategories', $fee.subcategory_id)
}
var $category {
value = db.get('categories', $subcategory.category_id)
}
var $source {
value = db.get('sources', $fee.source_id)
}
var $agency {
value = db.get('agencies', $source.agency_id)
}
// Then nested them properly using var.update
$fee|var.update: {
subcategory: {
...$subcategory,
category: $category
},
source: {
...$source,
agency: $agency
}
}
Impact: Relationships now properly included in every response. API went from returning incomplete data to being production-ready.
Issue #2: N+1 Query Performance Problem
AI Generated (GET /categories):
foreach ($categories as $category) {
// Query fees for EACH category individually - terrible for performance!
var $fee_count {
value = db.query('fees', {
where: { subcategory.category_id: $category.id }
}).count
}
}
The Problem: If you have 6 categories, this makes 6 separate database queries. Would fail under load.
My Optimization:
// Single query with proper aggregation
var $fee_counts {
value = db.query('fees', {
join: {
subcategories: { category_id: 'categories.id' }
},
group_by: 'categories.id',
select: {
category_id: 'categories.id',
fee_count: 'COUNT(fees.id)'
}
})
}
Impact: Reduced database queries from 6 to 1. Response time improved by ~60%.
Issue #3: Empty Results Bug
The Problem: When calling /fees with no filters, API returned {"items":[],"itemsTotal":0} despite having 87 fees in the database.
Root Cause: The where clause was too restrictive. When optional filters were null, the logical AND conditions were filtering out all records.
My Fix:
// Added explicit null/empty checks for each optional filter
where = (
($search_filter == null || $search_filter == '' ||
$db.fees.name includes? $search_filter ||
$db.fees.description includes? $search_filter) &&
($category_filter == null || $category_filter == '' ||
$db.category.slug ==? $category_filter ||
$db.category.name ==? $category_filter) &&
($state_filter == null || $state_filter == '' ||
$db.fees.meta.state ==? $state_filter)
)
Impact: API now correctly returns all fees when no filters are applied, and properly filters when they are.
Issue #4: Incorrect Sort Syntax
AI Generated:
sort = { "$db.fees.updated_at": "desc" }
The Problem: XanoScript doesn't use $db. prefix in sort blocks. Caused silent sorting failures.
My Fix:
sort = { fees.updated_at: "desc" }
Impact: Last updated fees now correctly sorted by timestamp.
What I Added Beyond AI
1. API Key Authentication System
function auth_api_key() {
// Extract and validate API key
var $api_key { value = get($http_query, "api_key") }
if (!$api_key) {
throw {
code: "ERROR_CODE_ACCESS_DENIED",
message: "Missing API Key"
}
}
// Query and validate
var $key_record {
value = db.get('api_keys', {
key: $api_key,
is_active: true
})
}
if (!$key_record) {
throw {
code: "ERROR_CODE_ACCESS_DENIED",
message: "Invalid or inactive API Key"
}
}
// Update usage stats
db.update('api_keys', $key_record.id, {
request_count: $key_record.request_count + 1,
last_request_at: now()
})
return $key_record
}
2. Standardized Error Responses
{
"code": "ERROR_CODE_ACCESS_DENIED",
"message": "Invalid or inactive API Key.",
"status": 401
}
3. Comprehensive Documentation
- Full API reference with request/response examples
- Quick Start guide (5 minutes to first API call)
- Code examples in JavaScript, Python, and cURL
- Data sources reference with official URLs
The Numbers: AI vs Human Contribution
| Metric | AI | Human |
|---|---|---|
| Time | 30 minutes | 4-5 hours |
| Code Generated | ~500 lines | ~300 lines |
| Critical Bugs Found | 0 | 5 |
| Production Features | Basic CRUD | Auth, optimization, error handling |
| Documentation | None | Complete |
Total Development Time: ~5 hours (would have been 10-12 hours without AI)
My Experience with Xano
What I Found Most Helpful
1. Visual Database Designer
Building the relational database schema with proper foreign keys was intuitive. The visual interface made it easy to see relationships between tables (fees → subcategories → categories → agencies).
2. Built-in API Testing
The "Run & Debug" feature in the API builder was invaluable. I could test endpoints immediately without deploying or using external tools. This caught bugs early.
3. XanoScript Flexibility
Having both visual blocks and code gave me the best of both worlds. For simple queries, I used the visual builder. For complex logic (like the authentication function), I wrote XanoScript directly.
4. One-Click Deployment
No server setup, no deployment configuration, no DevOps complexity. Just build and it's live. This saved hours of infrastructure work.
5. PostgreSQL Under the Hood
Knowing I had a real relational database with proper ACID guarantees gave me confidence. This isn't a toy database—it's production-grade.
Challenges I Faced
1. XanoScript Learning Curve
Coming from Java/JavaScript/Python, XanoScript syntax had quirks:
-
$variablevsvariablenaming conventions - When to use
evalblocks (spoiler: less than I thought) - Sort/filter syntax differences from standard SQL
Solution: Trial and error, plus reading Xano docs. The AI-generated code gave me examples to learn from, even when it had bugs.
2. Debugging AI-Generated Code
AI confidently generated invalid syntax ($db.{}) that looked plausible. Without testing, I would have shipped broken code.
Lesson Learned: Always test AI-generated code immediately. AI is great at structure but can miss framework-specific nuances.
3. Documentation Gaps
Some XanoScript features weren't well-documented online. I had to experiment to figure out the correct syntax for complex joins and aggregations.
Solution: Used the Xano community forums and tested different approaches until I found what worked.
4. N+1 Query Gotcha
It was easy to accidentally create performance problems with nested loops. The visual builder doesn't warn you about N+1 queries.
Solution: Always think about database query patterns. Single complex query > multiple simple queries.
What Made This Project Successful
The Winning Combination:
- ✅ Xano handled infrastructure and deployment
- ✅ AI generated boilerplate and structure quickly
- ✅ Human expertise fixed bugs, optimized performance, added production features
- ✅ Real data from official government sources made it valuable
None of these alone would have worked. But together? I built a production-ready API in ~5 hours that would have taken 2-3 days manually.
Would I Use Xano Again?
Absolutely. For rapid API development, especially for:
- Hackathons - Speed to deployment is unbeatable
- MVPs - Get to market fast, validate ideas
- Internal tools - Build CRUD APIs in hours, not days
- Prototypes - Prove concepts before investing in custom infrastructure
When I might not use it:
- Extremely high-scale applications requiring custom infrastructure
- Projects needing specific database features outside PostgreSQL
- Complex real-time features requiring WebSockets at scale
But for 90% of backend API projects? Xano + AI is a game-changer.
Impact & Future Plans
Who Can Use This API?
🏦 Fintech Companies:
- Budget calculators including government fees
- Payment platforms for government services
- Financial planning apps
📱 Mobile Apps:
- Citizen services super-apps
- Student planning tools
- Government service aggregators
🤖 Chatbots:
- WhatsApp bots for fee inquiries
- USSD services for feature phones
- AI government assistants
What's Next
Immediate (v1.1):
- Implement rate limiting (100 req/hour)
- Add webhook support for fee updates
- Create admin dashboard for data management
Future (v2.0):
- Expand to all 36 Nigerian states
- Historical fee tracking (price changes)
- Multi-language support
- Mobile SDK
Long-term Vision:
Transform into a comprehensive government services platform with payment processing, application tracking, and document verification.
Data Sources
All fees sourced from official government documents:
- NIMC: SERVICE_LEVEL_AGREEMENT_2025_.pdf
- NIS: Official Immigration Service SLA
- NECO: Official exam fees from neco.gov.ng
- JAMB: Payment services from jamb.gov.ng
- NERC/EKEDC: Official tariff plans from ekedp.com
Every fee includes verification_url linking to the official source for transparency.
Try It Now!
Test API Key: nga_b6cf98a60bda43ba8cf54af9dbd87260
Quick Test:
curl 'https://xmlb-8xh6-ww1h.n7e.xano.io/api:public/fees/search?q=passport&api_key=nga_b6cf98a60bda43ba8cf54af9dbd87260'
Full Documentation: GitHub Repository (includes Quick Start guide, complete API reference, and code examples)
Conclusion
This project proves that AI-assisted development works best as a partnership: AI provides speed and structure, humans provide critical thinking and domain expertise.
The Nigerian Government Fees API solves a real problem affecting millions. By centralizing scattered information into one well-documented API, we're removing barriers to accessing essential government services.
More importantly, this is just the beginning. With a solid foundation, this can grow into a platform that truly improves how Nigerian citizens interact with public services.
Questions? Want to contribute? Drop a comment below! 🚀
Top comments (0)