Gramps Web API Client
A simple Python client based on requests for interacting with a Gramps Web API server.
Warning
This is an experimental project for advanced users. The goal is to allow powerful batch operations on Gramps objects via the API. But only use it if you know what you are doing – you can easily break your Gramps database with this tool!
Install
pip install gramps-web-api-client
Quick start
First, instantiate the API instance:
from gramps_web_api_client import API
api = API(
host="https://my-gramps-web-instance.com",
basic_auth=("my_user", "my_password")
)
The client reuses a single HTTP session. It can also be used as a context manager, which closes the session when done:
with API(host="https://my-gramps-web-instance.com", basic_auth=("my_user", "my_password")) as api:
print(api.get_metadata())
Optional arguments are timeout (seconds per request, default 30) and session (a preconfigured requests.Session). Access tokens are fetched on first use and renewed automatically when they expire.
Python 3.10 or newer is required.
Methods
For each object type, the client has methods to iterate over, get, create, update and delete objects:
| Object type | Iterate | Get by handle / Gramps ID | Create | Update | Delete |
|---|---|---|---|---|---|
| Citation | iter_citations() |
get_citation(handle), get_citation_by_gramps_id(gramps_id) |
create_citation(data) |
update_citation(handle, data) |
delete_citation(handle) |
| Event | iter_events() |
get_event(handle), get_event_by_gramps_id(gramps_id) |
create_event(data) |
update_event(handle, data) |
delete_event(handle) |
| Family | iter_families() |
get_family(handle), get_family_by_gramps_id(gramps_id) |
create_family(data) |
update_family(handle, data) |
delete_family(handle) |
| Media object | iter_media() |
get_media(handle), get_media_by_gramps_id(gramps_id) |
– | update_media(handle, data) |
delete_media(handle) |
| Note | iter_notes() |
get_note(handle), get_note_by_gramps_id(gramps_id) |
create_note(data) |
update_note(handle, data) |
delete_note(handle) |
| Person | iter_people() |
get_person(handle), get_person_by_gramps_id(gramps_id) |
create_person(data) |
update_person(handle, data) |
delete_person(handle) |
| Place | iter_places() |
get_place(handle), get_place_by_gramps_id(gramps_id) |
create_place(data) |
update_place(handle, data) |
delete_place(handle) |
| Repository | iter_repositories() |
get_repository(handle), get_repository_by_gramps_id(gramps_id) |
create_repository(data) |
update_repository(handle, data) |
delete_repository(handle) |
| Source | iter_sources() |
get_source(handle), get_source_by_gramps_id(gramps_id) |
create_source(data) |
update_source(handle, data) |
delete_source(handle) |
| Tag | iter_tags() |
get_tag(handle) |
create_tag(data) |
update_tag(handle, data) |
delete_tag(handle) |
In addition:
| Method | Description |
|---|---|
search(query) |
Iterate over full-text search results |
get_metadata(include_surnames) |
Get metadata about the current instance |
Uploading and downloading media files is not supported yet, which is why there is no create_media.
Getting objects
get_* methods return a single object as a dictionary and raise a GrampsWebAPIError (status 404) if the handle does not exist. get_*_by_gramps_id methods return None if no object has that Gramps ID.
iter_* methods are generators that fetch results page by page. Their keyword arguments are passed as query parameters to the API; see the API documentation for all options. Commonly used ones are:
gramps_id: only return the object with this Gramps IDrules: a filter as a dictionary, using Gramps filter rule namesextend: include referenced objects, e.g.["event_ref_list", "citation_list"]or"all"profile: include a human-readable summary, e.g."self"or"all"keys/skipkeys: only return or leave out the given keysstrip: leave out empty values
Lists are sent comma-separated, dictionaries as JSON and booleans as true/false:
for person in api.iter_people(rules={"rules": [{"name": "IsMale"}]}, keys=["handle", "gramps_id"]):
...
search(query) iterates over full-text search results. Each result is a dictionary with the keys handle, object_type and object. It accepts the same kind of keyword arguments, e.g. type=["person", "note"] to limit the object types.
Creating, updating and deleting objects
data is always a dictionary following the Gramps Web API JSON conventions.
update_* methods fetch the object, merge data into it and save the result. The merge only works on top-level keys: a key you pass replaces the whole existing value. For example, passing {"primary_name": {"first_name": "John"}} replaces the entire primary name, deleting the surname and all other name fields. To change a nested value, fetch the object, modify it and pass the complete top-level value:
person = api.get_person("somehandle")
name = person["primary_name"]
name["first_name"] = "John"
api.update_person("somehandle", {"primary_name": name})
create_*, update_* and delete_* methods return the database transaction: a list of changes, each a dictionary with the keys type ("add", "update" or "delete"), _class, handle, old and new. To get the handle of a newly created object:
transaction = api.create_note({"text": {"_class": "StyledText", "string": "Hello"}})
handle = transaction[0]["handle"]
Errors
If the server returns an error, a GrampsWebAPIError is raised. It is a subclass of requests.HTTPError, with the HTTP status in status_code and the decoded JSON error body (if any) in data:
from gramps_web_api_client import GrampsWebAPIError
try:
api.get_person("nonexistent")
except GrampsWebAPIError as e:
print(e.status_code, e.data)
Examples
Removing all citations from an existing person:
api.update_person("somehandle", {"citation_list": []})
In this example, all other properties of the person (except citations) will remain the same.
Creating a new place:
api.create_place({
"name": {
"_class": "PlaceName",
"value": "Gotham City"
}
})
Iterating over people and adding a citation if a condition is satisfied:
for person in api.iter_people():
try:
surname = person["primary_name"]["surname_list"][0]["surname"]
except (KeyError, IndexError):
continue
if surname == "Garner":
api.update_person(
person["handle"],
{"citation_list": person["citation_list"] + ["some_citation_handle"]}
)
Development
This project uses uv:
uv sync # install the package and dev dependencies
uv run pytest # run the tests
uv run ruff check src tests
uv run ruff format src tests
uv run mypy
Metadata
Release files for gramps-web-api-client 0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| gramps_web_api_client-0.2.tar.gz | 14.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| gramps_web_api_client-0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 23.7 kB
Release files / gramps_web_api_client-0.2.tar.gz
| Download URL | gramps_web_api_client-0.2.tar.gz |
|---|---|
| Size | 14.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5b50f6d014d5f51763351c751a1a8b4f470c20b6beb7ccd0a8110ac317a71b21
|
|
BLAKE2b-256 checksum How to use checksums |
f2d314fd46341800cd174b1c8259e7a3751151565a7198952a600ccc1e540e5f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Sep 18, 2026.
Transparency logRelease files / gramps_web_api_client-0.2-py3-none-any.whl
| Download URL | gramps_web_api_client-0.2-py3-none-any.whl |
|---|---|
| Size | 9.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bd8b22df806c5b17d56f99c3917411ed942e876436c33939393b3a7551802279
|
|
BLAKE2b-256 checksum How to use checksums |
0e8d2ab99ca4809297b9b2af4b3148f9a161cb63c912e423d45e5547ff572563
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Sep 18, 2026.
Transparency log