Python client for the Crowdin API v2 and Crowdin Enterprise API v2 (PyPI: crowdin-api-client, import: crowdin_api).
Supports Python 3.8+, so write 3.8-compatible code: no X | Y unions, no match, and import TypedDict from crowdin_api.typing.
crowdin_api/client.py—CrowdinClient; one hand-written@propertyper resourcecrowdin_api/api_resources/<resource>/— one package per API resource:resource.py,types.py(request TypedDicts),enums.py,tests/test_<resource>_resources.pycrowdin_api/api_resources/abstract/resources.py—BaseResourcecrowdin_api/requester.py—APIRequester(session, retries, error mapping)
- Install:
pip install -r requirements/requirements-dev.txt - Test (all):
pytest—setup.cfgaddopts enforce a 95% coverage gate - Test (one file):
pytest crowdin_api/api_resources/<resource>/tests/test_*.py --no-cov— without--no-covthe coverage gate fails any partial run even when all tests pass - Lint (what CI runs):
flake8 . --count --show-source --statistics - Format:
pre-commit run --all-files(black-l 100, isort, flake8, xenon)
--doctest-modules is active: pytest imports every module in crowdin_api/, and any >>> in a docstring runs as a test.
Fetch the endpoint spec first (see Crowdin API reference below). Then:
- Implement the method on the
*Resourceclass incrowdin_api/api_resources/<resource>/resource.py:- List endpoints call
self._get_entire_data(method="get", path=..., params=...)sowith_fetch_all()pagination works; everything else callsself.requester.request(...). - Project-scoped methods take
projectId: Optional[int] = Noneand resolve it viaprojectId or self.get_project_id(). - Request body shapes go in
types.pyas TypedDicts; enum values inenums.py. Enums andSortingobjects can be passed straight intoparams/request_data— the custom JSON encoder serializes them, andNonevalues are stripped before sending. - End the docstring with
Link to documentation:and the developer.crowdin.com operation URL (pdoc publishes these).
- List endpoints call
- For a new resource, register it in three places: an import plus
__all__entry incrowdin_api/api_resources/__init__.py(alphabetical), a@propertyonCrowdinClientinclient.py(copy an existing property; use the enterprise-guard or per-platform variant when the API is Enterprise-only or differs by platform), and one tuple in each of the two parametrize lists incrowdin_api/tests/test_client.py. Some resource classes exist but were never registered (e.g.BranchesResource,StringCorrectionsResource) — "adding" one of those is exactly this registration work. - Test in the resource's
tests/dir: patch the requester with@mock.patch("crowdin_api.requester.APIRequester.request"), call the method, thenm_request.assert_called_once_with(method=..., path=..., ...)with the exact kwargs. Thebase_absolut_urlfixture (spelled without the second "e") provides the base URL. No test performs real HTTP.
A complete new resource touches ~8 files: the four package files (__init__.py is one line: __pdoc__ = {'tests': False}), the resource's test file, and the three registration files.
Before implementing or changing any endpoint, fetch its spec from the llms.txt indexes (pick by environment, then project type):
- https://support.crowdin.com/_llms-txt/api/crowdin/file-based.txt — Crowdin API, file-based projects (start here)
- https://support.crowdin.com/_llms-txt/api/crowdin/string-based.txt — Crowdin API, string-based projects
- https://support.crowdin.com/_llms-txt/api/enterprise/file-based.txt — Crowdin Enterprise API, file-based projects
- https://support.crowdin.com/_llms-txt/api/enterprise/string-based.txt — Crowdin Enterprise API, string-based projects
Each index links one spec file per route (e.g. .../api.projects.strings.get.txt) with the exact request and response shapes.
- Conventional Commits for commit messages and PR titles; CI lints PR titles.
- PRs target
main. - Keep the public API backward compatible; mark removals with
@deprecated(...)(from thedeprecatedpackage) instead of deleting. - Never edit
__version__incrowdin_api/__init__.py— the Release workflow bumps it.
A change is ready when:
pytestpasses, including the 95% coverage gate,flake8 . --count --show-source --statisticsis clean,- every new or changed endpoint method has a test asserting the exact requester call, and
- every new or changed public method's docstring ends with its documentation link.