Self-hosted service
felipeall/resumeio-to-pdf avatar
felipeall/resumeio-to-pdf

resumeio-to-pdf renders a resume.io export on your own machine

Download your resume from resume.io as PDF

818 stars161 forksPythonMIT

At a glance

What is it?
resumeio-to-pdf is a small Python CLI that turns the JSON behind a resume.io resume into a multi-page PDF by driving resume.io's own rendering worker locally. It is useful for previewing a resume outside the editor, and the project itself is explicit that it is a preview aid rather than a substitute for a paid account.
Who is it for?
Use resumeio-to-pdf when you already have a resume.io account and need a local, vector-text PDF of a specific revision without opening the editor. Skip it if your resume lives somewhere other than resume.io, or if you want an offline converter, since the worker chunks are downloaded from resume.io on first render and the tool calls out to resume.io for the resume JSON.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The PDF comes from resume.io's own rendering worker

resumeio-to-pdf does not reimplement resume.io's layout. The README states that every page is rendered with resume.io's own rendering worker, the same one the resume.io editor runs in the browser, and the reason given for that design is fidelity: the PDF keeps the original layout and keeps selectable, vector text. Vector text matters for a resume, because a flattened image cannot be selected, copied or parsed by an applicant tracking system.

The project is a Python package with a single console script. The packaging metadata gives the entry point as `resumeio_to_pdf.cli:main`, exposed as the command `resumeio-to-pdf`. The runtime dependencies are narrow: fastapi, jinja2, requests and uvicorn, each pinned to an exact version. The FastAPI and uvicorn pair is what backs the optional local web interface, while requests handles the calls out to resume.io.

The name of the tool and its topic tags reflect the direction of travel. The topic list covers cv, download, fastapi, ocr, pdf, resume, resumeio and searchable, so the searchable-text promise is part of the declared intent of the project rather than a side effect.

The rendering token no longer gets you past page one

Getting the input is the part of the workflow that has changed. The README describes a three-step path. You log in to resume.io, open the resumes endpoint at https://resume.io/api/app/resumes in the same browser, and note the `id` of the resume you want. You then open `https://resume.io/api/app/resumes/{id}` and save the page as `resume.json`.

What matters here is the reason the documentation gives for moving away from the older approach. It says the rendering token alone is no longer enough, because resume.io's image endpoint only ever serves the first page. A tool that renders a single page is not useful for a resume that runs to several, so the JSON route became the supported way in.

The instruction to use the same browser you logged in with is the practical detail. The endpoint is a session-backed page, so the JSON arrives in a browser that already holds your authentication rather than from an anonymous HTTP request. The command also accepts `-` instead of a path, which lets the saved JSON be piped in on standard input for anyone who wants to script the step.

Installing the command with uv puts it on PATH

The README lists two requirements before the install: uv, and Node.js 18 or newer on the PATH, because the rendering worker runs there. That Node dependency is the one that surprises people, since the tool itself is Python.

The install command installs the project straight from its Git repository:

bash
uv tool install git+https://github.com/felipeall/resumeio-to-pdf

That puts the `resumeio-to-pdf` command on your PATH. The README also names the two follow-up commands for the life of the installation, upgrade it with `uv tool upgrade resumeio-to-pdf` and remove it with `uv tool uninstall resumeio-to-pdf`.

Once installed, the first real use is the render subcommand with the saved JSON file:

bash
resumeio-to-pdf render resume.json

The output lands in the current folder as `<renderingToken>.pdf`. The README names three options around that: `-o my-resume.pdf` to choose the file name, `--locale` to override the language of the resume, and `-` instead of a path to read the JSON from standard input. The default file name being derived from the rendering token is worth knowing, because it means the output name carries no relation to the resume you picked.

serve and Docker cover the two other ways to run it

The same package can run as a local web service. The README gives a single command for it:

bash
resumeio-to-pdf serve

That serves the interface on http://127.0.0.1:8000. The port is bound to loopback, so the service is not exposed on the network by default.

There is also a Dockerfile at the root of the repository, and the README offers a build-and-run sequence for it:

bash
git clone https://github.com/felipeall/resumeio-to-pdf.git
cd resumeio-to-pdf
docker build -t resumeio-to-pdf .
docker run -p 127.0.0.1:8000:8000 resumeio-to-pdf

The Docker route exists because the image bundles Node.js, which removes the separate Node.js 18 requirement from the host. The Dockerfile itself shows the internals: it starts from `python:3.12.13-slim`, installs the `nodejs` package with apt, copies `pyproject.toml` and `uv.lock` in, runs `uv sync --no-editable --frozen --no-dev` twice so that dependencies are installed before the project code is copied, and finally exposes port 8000 with the command `resumeio-to-pdf serve --host 0.0.0.0`.

Note that the container binds to all interfaces inside the container, so the host side of the port mapping is what keeps it local. The published mapping is 127.0.0.1:8000 to 8000.

The disclaimer calls this a preview tool, and the README repeats it

The clearest limitation in this project is written by its author. The README carries a disclaimer saying the application is designed for preview purposes only, that use of it means agreeing to the applicable laws and regulations governing such services, and that the creators accept no responsibility for damages arising from its use.

It goes further and points readers to the resume.io pricing page, describing that as the way to get the resume downloading service through official channels. The README asks readers to support the platform by subscribing and discourages treating this application as a substitute for legitimate and paid access.

Practically, that shapes the decision. If you want a PDF of a resume you have already built on resume.io, this is a reasonable preview tool. If your goal is a free bulk route to resume downloads, the project itself steers you away from that use.

There is a second, quieter limitation. The renderer is not bundled. On the first render, the worker and its chunks, around 3.8 MB, are downloaded from resume.io into `~/.cache/resumeio-to-pdf`, or into `$RESUMEIO_WORKER_CACHE` when that variable is set. The README notes that nothing from resume.io is stored in this repository. So the tool runs locally, but it is not an offline converter, and the first render needs network access to resume.io.

Two environment variables control the update check and the worker cache

The installed command checks GitHub at most once a day and reports when a newer version is available. That behaviour is opt-out through an environment variable: setting `RESUMEIO_NO_UPDATE_CHECK=1` turns the check off. It matters for anyone running the tool in a script or in CI, where an outbound call to GitHub from a rendering command is an unexpected side effect.

The second variable is the one already mentioned, `$RESUMEIO_WORKER_CACHE`, which relocates the directory holding the downloaded worker chunks away from `~/.cache/resumeio-to-pdf`. On a shared machine or inside a container with a read-only home directory, pointing that variable at a writable path is the fix.

For development, the README documents a three command sequence:

bash
uv sync
uv run pytest
uv run ruff check .

That matches the project layout, which keeps the package in `resumeio_to_pdf/` with tests in `tests/`, and the packaging configuration in `pyproject.toml` where the dev dependency group pins black, pytest and ruff. The linting configuration is explicit as well, with a line length of 120 and a rule selection covering pyflakes, pycodestyle, isort, naming and comma style.

Version 2.1.1 ships pinned dependencies under the MIT license

The project is young and moving quickly. Three releases landed in October 2026: v2.0.0 on 2026-10-11, v2.1.0 on the same day a few minutes later, and v2.1.1 on 2026-10-19, which is the same date as the last push. The version in the project file is `2.1.1`, matching the latest tag.

That recent cadence is worth weighing against a dependency set that pins every runtime library to an exact version, fastapi, jinja2, requests and uvicorn all at fixed releases. Exact pins make a local installation reproducible, and they also mean a release of the tool is where those pins get moved. An upgrade is therefore a change to the lock file rather than a background resolution.

The Python requirement is explicit: `requires-python = '>=3.12'`. Nothing below 3.12 will work, and the Docker image pins `python:3.12.13-slim`.

Licensing is MIT, with the license file at the root as LICENSE.md. The MIT terms are permissive and short: the code may be used, modified and redistributed, provided the copyright notice and the permission notice travel with it, and the software is provided without warranty. That last point matters more than usual here, given that the project renders a document from a third-party service and the author has already disclaimed responsibility for its output.

Editorial conclusion

Use resumeio-to-pdf when you already have a resume.io account and need a local, vector-text PDF of a specific revision without opening the editor. Skip it if your resume lives somewhere other than resume.io, or if you want an offline converter, since the worker chunks are downloaded from resume.io on first render and the tool calls out to resume.io for the resume JSON. Before adopting it, check that Python 3.12 is available on the machine, since the project requires it, and read the disclaimer that describes the tool as preview only.

Frequently asked questions

How do I install resumeio-to-pdf?

The README lists uv and Node.js 18 or newer on the PATH as requirements, then installs the tool from Git with `uv tool install git+https://github.com/felipeall/resumeio-to-pdf`. That places the `resumeio-to-pdf` command on your PATH.

How do I get the resume JSON that resumeio-to-pdf needs?

Log in to resume.io, open https://resume.io/api/app/resumes in the same browser and note the `id` of the resume, then open `https://resume.io/api/app/resumes/{id}` and save the page as `resume.json`. The README says the rendering token alone is no longer enough because resume.io's image endpoint only serves the first page.

Does resumeio-to-pdf work offline?

No. On the first render, the worker and its chunks of about 3.8 MB are downloaded from resume.io into `~/.cache/resumeio-to-pdf`, or into `$RESUMEIO_WORKER_CACHE` if set, and the resume JSON itself comes from resume.io.

Can I run resumeio-to-pdf as a web app?

Yes. The `resumeio-to-pdf serve` command starts a local web interface on http://127.0.0.1:8000, and a Dockerfile at the repository root provides an image that bundles Node.js and exposes the same port.

Official sources

  1. felipeall/resumeio-to-pdf on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/felipeall-resumeio-to-pdf.svg)](https://hysenlabs.com/projects/felipeall-resumeio-to-pdf)

Was this useful?