A public REST API for searching ZIMRA Harmonised System (HS) codes using trigram similarity. The search endpoint is open — uploading HS code data is internal and not part of the public API.
Live: https://tools.digitaltouch.co.zw
- Fuzzy search across HS code and description using PostgreSQL trigram similarity
- Configurable similarity threshold
- Throttling on all endpoints
- Health check endpoint
- Python 3.12
- PostgreSQL 16
- Docker & Docker Compose (for containerised setup)
1. Clone and create a virtual environment
git clone [email protected]:DigitalTouchCode/Hs_codes_api.git
cd Hs_codes_api
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt2. Configure environment
cp .env.example .envEdit .env:
SECRET_KEY=your-secret-key
DEBUG=True
ALLOWED_HOSTS=localhost,127.0.0.1
DATABASE_URL=postgres://hsuser:hspass@localhost:5432/hsdb
HS_CODE_SEARCH_THRESHOLD=0.13. Run migrations
python manage.py migrate4. Run the development server
DJANGO_SETTINGS_MODULE=core.settings.development python manage.py runservercp .env.example .env # fill in values
docker compose up -d --buildThe API will be available at http://localhost:8001.
| Variable | Required | Default | Description |
|---|---|---|---|
SECRET_KEY |
Yes | — | Django secret key |
DEBUG |
Yes | — | True or False |
ALLOWED_HOSTS |
Yes | — | Comma-separated hostnames |
DATABASE_URL |
Yes | — | PostgreSQL connection string |
HS_CODE_SEARCH_THRESHOLD |
No | 0.1 |
Minimum trigram similarity score (0–1) |
GUNICORN_WORKERS |
No | 3 |
Number of Gunicorn worker processes |
GET /api/v1/hs-codes/?q=<query>
Search HS codes by description or code using trigram similarity. Results are ordered by relevance, with description weighted 2× higher than the code.
Parameters
| Parameter | Required | Description |
|---|---|---|
q |
Yes | Search term |
Response 200
[
{
"id": 1,
"hs_code": "8471.30.00",
"description": "Portable automatic data processing machines",
"created_at": "2026-01-01T00:00:00Z",
"hs_code_file": 1
}
]Response 400 — missing query
{"q": ["This query parameter is required."]}GET /api/v1/health/
Response 200
{"status": "healthy"}This project uses Black for code formatting, enforced via pre-commit.
Install pre-commit hooks
pip install pre-commit
pre-commit installRun manually
black .Check without modifying
black --check .Black is pinned at 24.8.0 in .pre-commit-config.yaml.
Run all tests
python manage.py test appRun a specific test class
python manage.py test app.tests.HsCodeUploadViewTestRun a single test
python manage.py test app.tests.HsCodeUploadViewTest.test_upload_valid_csv_returns_201Test coverage by area
| Area | Tests |
|---|---|
| File decoding | UTF-8, BOM stripping, bad encoding |
| CSV parsing | Valid CSV, missing columns, empty file |
| Object building | Blank row skipping, whitespace trimming |
| Upload service | Full flow, duplicates, partial duplicates |
| Upload view | 201/400 responses, duplicate handling |
| Search view | Missing q, fuzzy match, no results |
| Health check | 200 response, no auth required |
| Model | Unique constraint, cascade delete |
| Permissions | Admin/Staff allowed, unknown role/unauthenticated denied |
Deployment is handled via GitHub Actions. On every push to main:
- The CI workflow runs all 40 tests against a PostgreSQL service container.
- On CI success, the Deploy Production workflow SSHs into the VPS and deploys the latest build.
Required GitHub Secrets
| Secret | Description |
|---|---|
VPS_HOST |
VPS IP or hostname |
VPS_USER |
SSH username |
VPS_SSH_KEY |
Private SSH key |
VPS_PORT |
SSH port (usually 22) |
The deploy script polls GET /api/v1/health/ for up to 100 seconds after startup before marking the deployment as succeeded or failed.