LandVerify API Documentation
Securely submit and track land verification requests via our REST API.
Base URL: https://app.landverify.ng
Quick Start
1. Get API Key
Contact support@landverify.ng
2. Submit Request
POST to /verification/submit
3. Poll for Results
GET from /findings endpoint
Authentication
Include your API key in the request header for all endpoints:
x-api-key: YOUR_API_KEYSecurity Warning: Never expose your API key in public repositories, client-side code, or version control.
File Upload Guide
Files could be Base64 data URIs, public URLs, or other 3rd party file hosts.
File Requirements:
- • Maximum size: 10MB per file
- • Supported formats: PNG, JPG, JPEG, PDF
- • Required for: Full verification (
paymentType: full)
Converting Files to Base64
// Convert File to Base64
function fileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
}
// Usage example
const input = document.querySelector('input[type="file"]');
input.addEventListener('change', async (e) => {
const file = e.target.files[0];
const base64 = await fileToBase64(file);
console.log(base64); // "data:image/png;base64,iVBORw0KG..."
});1. Submit a Verification Request
Submit a new land verification request.
🟢 Regular Verification
paymentType: regular
Files are optional. Basic address verification without document review.
🔵 Full Verification
paymentType:full
Files are required. Complete verification with document review.
Request Parameters
| Field | Type | Required | Description |
|---|---|---|---|
address | string | ✓ | Property address to verify |
userId | number | ✓ | User ID of the account created via LandVerify |
paymentType | string | ✓ | regular or full |
files | string[] | * | Array of base64 data URIs (required for full) |
paymentAmount | number | Amount in local currency | |
paymentStatus | string | pending or completed | |
lga | string | Local Government Area | |
state | string | State name | |
landsize | string | Size of property (e.g., 500sqm) | |
latitude | number | GPS latitude | |
longitude | number | GPS longitude |
Example Request (Full Verification)
curl -X POST "https://app.landverify.ng/api/verification/submit" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"address": "123 Main St, Lagos",
"paymentType": "full",
"paymentAmount": 15000,
"paymentStatus": "completed",
"files": [
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8Xw8AAoMBg6d9fZcAAAAASUVORK5CYII="
],
"userId": 3386,
"lga": "Ikeja",
"state": "Lagos",
"landsize": "500sqm",
"latitude": 6.5244,
"longitude": 3.3792
}'Success Response (200)
{
"status": "SUBMITTED",
"verificationId": "2e5ab633-ea41-4d99-809c-efbf1e1e6925",
"partnerId": 3,
"paymentType": "full",
"uploadedFiles": [
"https://res.cloudinary.com/dsni5uhop/image/upload/v17280123/landverify_uploads/doc1.jpg"
],
"failedUploads": []
}Response Fields:
- •
status- Current status: SUBMITTED, IN_PROGRESS, or COMPLETED - •
verificationId- Unique ID for polling findings - •
partnerId- ID of assigned verification partner - •
uploadedFiles- Array of successfully uploaded file URLs - •
failedUploads- Array of failed uploads with reasons
2. Get Verification Findings
Poll this endpoint to check for verification results. Use the verificationId from the submit response.
Example Request
curl "https://app.landverify.ng/api/verification-requests/2e5ab633-ea41-4d99-809c-efbf1e1e6925/findings" \
-H "x-api-key: YOUR_API_KEY"Response (In Progress)
{
"findings": null,
"status": "SUBMITTED",
"updatedAt": "2025-10-05T10:30:00.000Z"
}Response (Complete)
{
"findings": {
"comments": "Property verified. Clean title with no encumbrances. Suitable for development with standard zoning approvals.",
"isRegisteredTitledVerified": true,
"isPropertyFreeOfAcquisition": true,
"DoesAddressMatchSurvey": true,
"erosionOrFloodRisk": false,
"locatedInMixedArea": true,
"suitableTopography": true
// ...additional fields
},
"status": "COMPLETED",
"updatedAt": "2025-10-05T12:45:00.000Z"
}Polling Guidelines:
- ✓ Poll every 30-60 seconds
- ✓ Stop when
status === COMPLETEDandfindings !== null - ✓ Implement exponential backoff on errors
- ✓ Set maximum polling duration (e.g., 30 minutes)
Complete Implementation Example
// Complete End-to-End Example
import { useState } from 'react';
function LandVerification() {
const [verificationId, setVerificationId] = useState(null);
const [findings, setFindings] = useState(null);
const [status, setStatus] = useState('idle');
// Step 1: Convert files to base64
const handleFileUpload = async (files) => {
const base64Files = await Promise.all(
Array.from(files).map(file => fileToBase64(file))
);
return base64Files;
};
// Step 2: Submit verification request
const submitVerification = async (address, files) => {
setStatus('submitting');
const base64Files = await handleFileUpload(files);
const response = await fetch('/api/verification/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'YOUR_API_KEY'
},
body: JSON.stringify({
address,
files: base64Files,
userId: 3386, // Your user's ID
paymentType: 'full',
paymentAmount: 15000,
paymentStatus: 'completed',
lga: 'Ikeja',
state: 'Lagos',
landsize: '500sqm'
})
});
const data = await response.json();
setVerificationId(data.verificationId);
setStatus('polling');
// Step 3: Start polling for results
startPolling(data.verificationId);
};
// Step 3: Poll for findings
const startPolling = (id) => {
const pollInterval = setInterval(async () => {
const response = await fetch(
`/api/verification-requests/${id}/findings`,
{ headers: { 'x-api-key': 'YOUR_API_KEY' } }
);
const data = await response.json();
// Stop polling when complete
if (data.status === 'COMPLETED' && data.findings) {
setFindings(data.findings);
setStatus('completed');
clearInterval(pollInterval);
}
}, 30000); // Poll every 30 seconds
// Cleanup after 30 minutes
setTimeout(() => clearInterval(pollInterval), 30 * 60 * 1000);
};
return (
<div>
{status === 'idle' && <button>Start Verification</button>}
{status === 'submitting' && <p>Submitting...</p>}
{status === 'polling' && <p>Waiting for results...</p>}
{status === 'completed' && (
<div>
<h3>Verification Complete!</h3>
<p>{findings.comments}</p>
<p>Registered Title: {findings.isRegisteredTitledVerified ? '✓' : '✗'}</p>
</div>
)}
</div>
);
}React Polling Hook
Production-ready React hook with automatic cleanup and timeout handling:
// JavaScript (React) polling for findings
import { useEffect, useState } from "react";
function useFindingsPolling(verificationId, apiKey, pollInterval = 30000) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
if (!verificationId || !apiKey) return;
let isMounted = true;
let intervalId;
let timeoutId;
const fetchFindings = async () => {
setLoading(true);
setError(null);
try {
const res = await fetch(
`/api/verification-requests/${verificationId}/findings`,
{ headers: { "x-api-key": apiKey } }
);
if (!res.ok) throw new Error(`Error: ${res.status}`);
const result = await res.json();
if (isMounted) {
setData(result);
// Stop polling if completed
if (result.status === 'COMPLETED' && result.findings) {
clearInterval(intervalId);
clearTimeout(timeoutId);
}
}
} catch (err) {
if (isMounted) setError(err.message || "Unknown error");
} finally {
if (isMounted) setLoading(false);
}
};
fetchFindings();
intervalId = setInterval(fetchFindings, pollInterval);
// Stop polling after 30 minutes
timeoutId = setTimeout(() => {
clearInterval(intervalId);
if (isMounted) setError('Polling timeout - verification may still be in progress');
}, 30 * 60 * 1000);
return () => {
isMounted = false;
clearInterval(intervalId);
clearTimeout(timeoutId);
};
}, [verificationId, apiKey, pollInterval]);
return { ...data, loading, error };
}Error Handling
Client Errors (4xx)
400- Bad Request (missing required fields)401- Unauthorized (invalid API key)404- Not Found (invalid verification ID)429- Too Many Requests (rate limit exceeded)
Server Errors (5xx)
500- Internal Server Error503- Service Unavailable
Example Error Response
{
"message": "Missing required fields: address and userId",
"error": "Bad Request"
}Need API Access?
Request your API keys (test and production) to start integrating with LandVerify.
