Skip to main content

buildstatus codecov PyPI version PyPI - Python Version PyPI - License PyPI - Format PyPI - Wheel

contactlookup

Self hosted contacts API for searching through a VCF file for contact records

Installation

Using pip:

pip install contactlookup

Using poetry:

poetry add contactlookup

Using pipenv:

pipenv install contactlookup

Quick Start

Running as a Docker container

Serve the contacts in the accompanying sample contacts.vcf file:

docker build -t contactlookup .
docker run -p 127.0.0.1:8000:8000 contactlookup
# Daemonize the container
docker run -d -p 127.0.0.1:8000:8000 contactlookup

To serve your own VCF file, mount it at /app/contacts.vcf. Mounting it read-only is a good idea:

docker run -d -p 127.0.0.1:8000:8000 \
  -v /path/to/contacts.vcf:/app/contacts.vcf:ro \
  contactlookup

To make it reachable from other machines, publish the port on all interfaces and require an API key:

docker run -d -p 8000:8000 \
  -e CONTACTLOOKUP_API_KEY="$(openssl rand -hex 32)" \
  -v /path/to/contacts.vcf:/app/contacts.vcf:ro \
  contactlookup

The image runs as a non-root user and has a health check on /health.

To stop the container:

docker ps # Get the container ID
docker stop <container_id>

Running as a CLI application

As a script:

contactlookup --help
contactlookup # Start the API server, and serve the contacts in the accompanying contacts.vcf file
contactlookup -f /path/to/contacts.vcf # Start the API server, and serve the contacts in the VCF file
contactlookup -f /path/to/contacts.vcf --host 0.0.0.0 --port 9000 # listen on all interfaces, port 9000

The CLI listens on 127.0.0.1:8000 by default.

Configuration

Setting CLI flag Environment variable Default
Address to listen on --host CONTACTLOOKUP_HOST 127.0.0.1 (0.0.0.0 in Docker)
Port --port CONTACTLOOKUP_PORT 8000
API key CONTACTLOOKUP_API_KEY none (API is open)

When CONTACTLOOKUP_API_KEY is set, every /contacts request needs an X-API-Key header:

curl -H "X-API-Key: $CONTACTLOOKUP_API_KEY" http://localhost:8000/contacts/fname/jeff

/, /health and /docs stay open. The key is left out of the CLI flags on purpose, so it doesn't show up in your shell history or ps. If you listen on anything other than localhost without a key, the app prints a warning at startup.

Responses

  • 200 with {"contacts": [...]} for searches, which is an empty list when nothing matches
  • 404 for /contacts/{id} when there's no contact with that id
  • 401 when an API key is configured and the header is missing or wrong
  • 503 while the contacts aren't loaded yet

As a module:

python -m contactlookup --help

Using the API

You can search for contacts by

  • first name,
  • phone number, or
  • email address.

The search is case-insensitive. Partial matches are not yet supported.

Search using web browser

Open your web browser and navigate to http://localhost:8000/docs to see the API documentation. Depending on your setup, you may need to replace localhost.

Search using curl

Search by email
curl -X 'GET' \
  'http://localhost:8000/contacts/email/jeffnewman@example.net' \
  -H 'accept: application/json'
Search by phone number
curl -X 'GET' \
  'http://localhost:8000/contacts/phone/+993-547-8840782' \
  -H 'accept: application/json'

The use of +, -, ., and () in the phone number is optional. Response:

{
    "contacts": [
        {
            "id": 1,
            "first_name": "KRISTEN",
            "last_name": "PEREZ",
            "other_names": null,
            "company": "Crescendo Associates",
            "title": null,
            "nickname": "NICKNAME",
            "birthday": "1966-08-19",
            "phone_numbers": [
                {
                    "id": null,
                    "number": "7194259182505",
                    "contact_id": 1,
                    "type": null
                },
                {
                    "id": null,
                    "number": "9935478840782",
                    "contact_id": 1,
                    "type": null
                }
            ],
            "addresses": [
                {
                    "id": null,
                    "street": "36346 Hall Stream",
                    "city": "CHRISTOPHERSTAD",
                    "state": "CA",
                    "postal_code": "73297",
                    "contact_id": 1,
                    "type": null,
                    "country": "USA"
                },
                {
                    "id": null,
                    "street": "196 Purple Sage Cres",
                    "city": "WINNIPEG",
                    "state": "MB",
                    "postal_code": "R3X 1V7",
                    "contact_id": 1,
                    "type": null,
                    "country": "CA"
                }
            ],
            "emails": [
                {
                    "id": null,
                    "email": "jacksonkimberly@example.net",
                    "type": null,
                    "contact_id": 1
                }
            ]
        }
    ]
}
Search by first name
curl -X 'GET' \
  'http://localhost:8000/contacts/fname/jeff' \
  -H 'accept: application/json'

Results are returned in JSON format:

{
    "contacts": [
        {
            "id": 3,
            "first_name": "JEFF",
            "last_name": "",
            "other_names": null,
            "company": "Viagenie",
            "title": null,
            "nickname": null,
            "birthday": "1949-01-29",
            "phone_numbers": [
                {
                    "id": null,
                    "number": "3632144414254",
                    "contact_id": 3,
                    "type": null
                }
            ],
            "addresses": [
                {
                    "id": null,
                    "street": "2360 Bean Tunnel",
                    "city": "SOUTH JAMES",
                    "state": "DC",
                    "postal_code": "96916",
                    "contact_id": 3,
                    "type": null,
                    "country": "MONTSERRAT"
                },
                {
                    "id": null,
                    "street": "26767 Khan Dam",
                    "city": "EAST MARISAMOUTH",
                    "state": "MA",
                    "postal_code": "26701",
                    "contact_id": 3,
                    "type": null,
                    "country": "BRAZIL"
                },
                {
                    "id": null,
                    "street": "63932 Natasha Fords",
                    "city": "DEBRAVIEW",
                    "state": "VT",
                    "postal_code": "22957",
                    "contact_id": 3,
                    "type": null,
                    "country": "NAMIBIA"
                }
            ],
            "emails": [
                {
                    "id": null,
                    "email": "allentaylor@example.net",
                    "type": null,
                    "contact_id": 3
                },
                {
                    "id": null,
                    "email": "daniel03@example.net",
                    "type": null,
                    "contact_id": 3
                },
                {
                    "id": null,
                    "email": "martinezveronica@example.net",
                    "type": null,
                    "contact_id": 3
                }
            ]
        },
        {
            "id": 4,
            "first_name": "JEFF",
            "last_name": "NEWMAN",
            "other_names": null,
            "company": "Hollywood",
            "title": null,
            "nickname": null,
            "birthday": "1955-03-29",
            "phone_numbers": [
                {
                    "id": null,
                    "number": "161555531122",
                    "contact_id": 4,
                    "type": null
                }
            ],
            "addresses": [
                {
                    "id": null,
                    "street": "123 Hollywood St",
                    "city": "BEVERLEY-HILLS",
                    "state": "CA",
                    "postal_code": "22957",
                    "contact_id": 4,
                    "type": null,
                    "country": "USA"
                }
            ],
            "emails": [
                {
                    "id": null,
                    "email": "jeffnewman@example.net",
                    "type": null,
                    "contact_id": 4
                }
            ]
        }
    ]
}

Metadata

Release files for contactlookup 3.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for contactlookup 3.0.0
File Size Uploaded Provenance Actions
contactlookup-3.0.0.tar.gz 31.1 kB With provenance Details for contactlookup-3.0.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for contactlookup 3.0.0
File Interpreter ABI Platform Actions
contactlookup-3.0.0-py3-none-any.whl Python 3 none any Details for contactlookup-3.0.0-py3-none-any.whl

Total release size: 60.8 kB

Release files / contactlookup-3.0.0.tar.gz

Download URL contactlookup-3.0.0.tar.gz
Size 31.1 kB
Tags Source
SHA-256 checksum
How to use checksums
3ee8a277440ecf3881890952c18d24ad7ca2a62a09d3426c3c1be1844b8fc3cf
BLAKE2b-256 checksum
How to use checksums
644e0af749de70aee269af4156c644ce10ef25b6f9926801f47dbe058e2ee608
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 3, 2026.

Transparency log

Release files / contactlookup-3.0.0-py3-none-any.whl

Download URL contactlookup-3.0.0-py3-none-any.whl
Size 29.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6834e12ea64cab13cae746ddf460b9998a3acc775c3b4d24625bcb6e302a6ea7
BLAKE2b-256 checksum
How to use checksums
c31c3d478a348ad356f02beff6ac34791aa4b601e712bc187ef65c7c78ee691b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 3, 2026.

Transparency log

Security for contactlookup 3.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for contactlookup-3.0.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 3, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page