API Troubleshooting & Best Practices

Fix common Township Canada API errors including Forbidden responses, CORS issues, rate limiting, and empty results. Includes debugging tips and integration patterns.

Getting errors from the Township Canada API? This guide covers the most common issues developers run into — authentication failures, CORS blocks, rate limits, and unexpected empty responses — with clear fixes for each one.

Common errors and what they mean

Forbidden (HTTP 403)

{ "message": "Forbidden" }

Cause: Your API key is missing, invalid, or not included correctly.

Fix:

  1. Confirm your API key exists: go to My Account → API Access
  2. Include it in the X-API-Key header (not as a URL parameter)
  3. Test with curl:
curl -H "X-API-Key: YOUR_KEY" \
  "https://developer.townshipcanada.com/search/legal-location?q=SW-25-24-1-W5"

If you get a valid JSON response, your key works. If you still get Forbidden, the key may have been rotated — generate a new one from your account page.

Too Many Requests (HTTP 429)

Cause: You exceeded the rate limit for your API tier.

TierRate Limit
Build1 request/second
Scale5 requests/second
Enterprise25 requests/second

Fix:

  • Add a delay between requests: await new Promise(r => setTimeout(r, 1000)) in JavaScript, or time.sleep(1) in Python
  • Use the Batch API instead of looping single calls — it processes up to 100 records per request
  • If you consistently need higher throughput, contact us about upgrading your tier

CORS Policy Error

Access to fetch at 'https://developer.townshipcanada.com/...' has been blocked by CORS policy

Cause: You are calling the API directly from a browser (client-side JavaScript). The API is designed for server-to-server calls and does not include CORS headers by default.

Fix options:

  1. Proxy through your backend — Make the API call from your server (Node.js, Python, etc.) and return the result to your frontend
  2. Contact us — If you need direct browser access, we can add your domain to the CORS allowlist

Empty Response (no features)

The API returns a valid HTTP 200 response, but the result contains no features or coordinates.

Possible causes:

  • The legal description does not exist (e.g., a township/range combination that was never surveyed)
  • The description is at a resolution not supported for that region
  • Format is slightly wrong — the API is stricter than the web app

Debugging steps:

  1. Try the same description in the web app search box — does it work there?
  2. Check that you included the full meridian (e.g., W5 not just 5)
  3. Verify component ranges: LSD 1–16, Section 1–36, Township 1–126, Range 1–34
  4. Check the Coverage Guide for resolution limits by region

Database Errors (28000, 08006)

Errors with codes like 28000 or 08006 indicate a service disruption, not an issue with your code.

What to do:

  • Check the status page for incident reports
  • Wait a few minutes and retry
  • If the issue persists beyond 15 minutes, contact us

API key setup

Quick recap of key setup (see the full API key management guide for details):

  1. Find your key: My Account → API Access
  2. Include in requests: Add the X-API-Key header to every request
  3. Test before coding: Use curl or Postman to confirm the key works
  4. Name your keys: Use descriptive names like "production-backend" or "staging-test" to keep track of which key is used where
  5. Rotate regularly: Generate new keys periodically and phase out old ones

Rate limiting best practices

Add delay between requests

// JavaScript — simple delay
for (const query of queries) {
  const result = await fetch(url + query, { headers });
  await new Promise((r) => setTimeout(r, 1000));
}
# Python — simple delay
import time
for query in queries:
    result = requests.get(url + query, headers=headers)
    time.sleep(1)

Use exponential backoff on 429

async function fetchWithBackoff(url, headers, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const res = await fetch(url, { headers });
    if (res.status !== 429) return res;
    await new Promise((r) => setTimeout(r, 1000 * Math.pow(2, i)));
  }
  throw new Error("Rate limit exceeded after retries");
}

Use the Batch API for bulk work

Instead of looping 1,000 single calls (which takes 17+ minutes at 1/sec), use the Batch API to process 100 records per call — the same 1,000 records take 10 calls.

See the Batch API Guide for chunking examples in Node.js and Python.

Format differences: web app vs. API

The web app's search box is more forgiving than the API:

IssueWeb AppAPI
Missing "W" on meridianMay auto-correctReturns empty
Extra spacesHandledMay cause issues
Mixed caseHandledHandled
Partial descriptionsShows suggestionsReturns empty

When building API integrations, always normalize your input first:

  • Include dashes between all components
  • Add the "W" prefix on meridians
  • Remove extra text, parentheses, and place names
  • Trim whitespace

Integration patterns

Caching

If your application looks up the same legal descriptions repeatedly, cache the results. Legal land descriptions map to fixed coordinates — the result for SW-25-24-1-W5 will not change.

Error handling

Always handle these response codes:

  • 200 — Success, parse the result
  • 400 — Bad request, check your input format
  • 403 — Authentication failed, check your API key
  • 429 — Rate limited, wait and retry
  • 500/502/503 — Server issue, retry after a short delay

OpenAPI spec

Download the OpenAPI specification at /openapi.yaml and import it into Postman, Swagger UI, or Insomnia for interactive testing and documentation.