LandVerify API Documentation

Securely submit and track land verification requests via our REST API. All endpoints require authentication via an API key for enhanced security.

Authentication

Include your API key in the request header for all authenticated endpoints:

x-api-key: YOUR_API_KEY

Security Note: Never expose your API key in public repositories or client-side code.

1. Submit a Verification Request

POST /api/verification/submit

Submit a new land verification request. Requires a valid user ID, address, and at least one file (Cloudinary URL).

Request Headers

Content-Type: application/json\nx-api-key: YOUR_API_KEY

Request Body

{
  "address": "123 Main St, Lagos",
  "files": ["https://res.cloudinary.com/demo/image/upload/sample.jpg"],
  "userId": 32344386,
  "lga": "Ikeja",         // optional
  "state": "Lagos",       // optional
  "landsize": "500sqm",   // optional
  "latitude": 6.5244,     // optional
  "longitude": 3.3792     // optional
}

Success Response (200)

{
  "status": "SUBMITTED",
  "findings": null,
  "verificationId": "2e5ab633-ea41-4d99-809c-efbf1e1e6925",
  "partnerId": 19
}

Example cURL

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",
    "files": ["https://res.cloudinary.com/demo/image/upload/sample.jpg"],
    "userId": 3386
  }'

2. Get Verification Request Details

GET /api/verification-requests/{id}/requests

Fetches the details of a specific verification request by its ID.

Success Response (200)

{
  "id": "2e5ab633-ea41-4d99-809c-efbf1e1e6925",
  "address": "123 Main St, Lagos",
  "landsize": "500sqm",
  "latitude": 6.5244,
  "longitude": 3.3792,
  "lga": "Ikeja",
  "state": "Lagos",
  "status": "SUBMITTED"
}

Example cURL

curl "https://app.landverify.ng/api/verification-requests/2e5ab633-ea41-4d99-809c-efbf1e1e6925/requests" \
  -H "x-api-key: YOUR_API_KEY"

3. Get Verification Findings (Polling Endpoint)

GET /api/verification-requests/{id}/findings

Fetches the current findings and status for a specific verification request.

⚠️ Requires API key and is rate-limited (30 requests per minute)

Success Response (200)

{
  "findings": {
    "comments": "This property at 123 Main Street has been verified, you will have 480 characters here detailing the property findings and expert recommendations.",
    "isRegisteredTitledVerified": true,
    "isPropertyFreeOfAcquisition": false,
    "DoesAddressMatchSurvey": false,
    "erosionOrFloodRisk": false,
    "locatedInMixedArea": true,
    "suitableTopography": true,
    // ...15+ other factors.
  },
  "status": "COMPLETED",
  "updatedAt": "2025-06-26T12:00:00.000Z"
}

Example cURL

curl "https://app.landverify.ng/api/verification-requests/2e5ab633-ea41-4d99-809c-efbf1e1e6925/findings" \
  -H "x-api-key: YOUR_API_KEY"

4. Rate Limiting

The /findings endpoint is rate-limited to 30 requests per minute per API key.

Exceeding this limit will return a 429 Too Many Requests error.

5. Error Handling

All endpoints return appropriate HTTP status codes and JSON error messages:

Client Errors (4xx)

  • 400 - Bad Request
  • 404 - Not Found
  • 405 - Method Not Allowed
  • 429 - Too Many Requests

Server Errors (5xx)

  • 500 - Internal Server Error
{
  "message": "Findings not found for this verification request"
}

6. Polling Recommendations

Best Practices

  • Polling Interval: Poll the /findings endpoint every 30–60 seconds to check for updates.
  • Stop Condition: Stop polling when status changes from "SUBMITTED" or when findings is no longer null.
  • Error Handling: Implement exponential backoff for failed requests to avoid hitting rate limits.

7. Frontend Integration Examples

JavaScript (React Hook)

// 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;
    
    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);
      } catch (err) {
        if (isMounted) setError(err.message || "Unknown error");
      } finally {
        if (isMounted) setLoading(false);
      }
    };

    fetchFindings();
    intervalId = setInterval(fetchFindings, pollInterval);

    return () => {
      isMounted = false;
      clearInterval(intervalId);
    };
  }, [verificationId, apiKey, pollInterval]);

  return { ...data, loading, error };
}

TypeScript (React Hook with Types)

// TypeScript (React) polling for findings
import { useEffect, useState } from "react";

interface FindingsResponse {
  findings: any;
  status: string;
  updatedAt: string;
}

function useFindingsPolling(
  verificationId: string,
  apiKey: string,
  pollInterval: number = 30000
): FindingsResponse & { loading: boolean; error: string | null } {
  const [data, setData] = useState<FindingsResponse | null>(null);
  const [loading, setLoading] = useState<boolean>(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    if (!verificationId || !apiKey) return;
    
    let isMounted = true;
    let intervalId: NodeJS.Timeout;
    
    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: FindingsResponse = await res.json();
        if (isMounted) setData(result);
      } catch (err: any) {
        if (isMounted) setError(err.message || "Unknown error");
      } finally {
        if (isMounted) setLoading(false);
      }
    };

    fetchFindings();
    intervalId = setInterval(fetchFindings, pollInterval);

    return () => {
      isMounted = false;
      clearInterval(intervalId);
    };
  }, [verificationId, apiKey, pollInterval]);

  return { ...data, loading, error };
}

Usage Tip: These hooks automatically handle polling, cleanup, and error states. Simply pass your verification ID and API key to start monitoring for results.

Need an API Key? You will need both a test and live API key to use the LandVerify API.
Please reach out to support@landverify.ng or +234 902 962 8530 to request keys.