Self-hosted service
pjialin/py12306 avatar
pjialin/py12306

py12306: a distributed ticket-buying assistant for China Railway's 12306

🚂 12306 购票助手,支持集群,多账号,多任务购票以及 Web 页面管理

14,963 stars3,576 forksPythonApache-2.0

At a glance

What is it?
py12306 is a Python assistant that queries seat availability on 12306 across multiple dates and accounts, places orders automatically, and can run as a Redis-backed cluster with a web console. It is a self-hosted tool for people who understand what they are automating.
Who is it for?
py12306 suits users who can read Python, keep a config file current, and accept that captcha handling and notification channels depend on third-party services that may change without notice. It is a poor fit for anyone wanting a managed service, or for anyone unwilling to run it from a network where the 12306 query endpoints are not rate-limited.
Can I use it commercially?
Yes. Apache-2.0 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 12 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 September 29, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What py12306 automates, and for whom

The 12306 booking site is built around a narrow window: tickets for a given train are released at a fixed time, and the useful seats disappear within seconds. py12306 exists to sit in that window. The README describes it as a distributed, multi-account, multi-task ticket assistant, and the feature list is concrete about what that means: querying remaining tickets across multiple dates, automatic captcha solving followed by order placement, restoring user state, and notifications by phone voice call, email, WeChat, DingTalk and Telegram.

The intended user is someone comfortable running a Python service on their own machine or server. There is no hosted version and no account to sign up for. The repository ships a Dockerfile, a docker-compose.yml.example, a Gitpod button, and example environment files, which tells you the maintainers expect the operator to own the deployment. The audience is narrow by design: people who want the query loop running continuously and who are willing to keep a configuration file in sync with it.

The architecture: a query loop, a separate login, and Redis for coordination

The README draws one line that matters more than any other: querying and login are separate operations, and querying does not depend on whether the user is logged in. That split is why the assistant can poll seat availability from an unauthenticated context and only touch the account when a ticket actually needs to be ordered. It also explains the anti-blocking note in the README, which warns that running the query traffic from common cloud providers tends to get the IP restricted and suggests running from another network environment.

Cluster mode depends on Redis. The README lists the properties it supports: one master with several slaves running at once, automatic promotion of a slave when the master goes down, automatic demotion back to the real master when it returns, configuration synchronised from the master to all slaves, live config changes on the master without restarting slaves, and real-time message sync from slaves to the master. A dedicated slave config example, env.slave.py.example, is provided and started with python main.py -c env.slave.py.

The web console is a Flask application served on port 8008 by default. The README states it currently covers users, tasks and live logs, and that more features are planned. That is an honest scope statement: treat the console as an operations view rather than a full management product.

Installing py12306 and running a first configuration test

The README requires Python 3.6 or above and notes that other versions have not been tested. The Dockerfile pins python:3.6.6-slim, so the container path is the closest thing to a known-good interpreter.

Clone the repository and install the pinned dependencies. The requirements file points pip at a Tsinghua mirror as its index, so the install resolves against that mirror unless you override it.

bash
git clone https://github.com/pjialin/py12306

pip install -r requirements.txt

Copy the example environment file to env.py, then edit it. This is where accounts, tasks, notification settings and the captcha mode live.

bash
cp env.py.example env.py

The README is explicit that the paid captcha provider, Ruokuai, has stopped serving, so the free captcha mode is currently the only option. Free mode is wired to a shared captcha platform at https://py12306-helper.pjialin.com. Voice notification uses a provider bought through the Alibaba Cloud API marketplace, and its appcode goes into the configuration.

Before letting it run, use the built-in checks. The -t flag tests configuration, including account detection, passenger information and station detection. Add -n when you also want a notification test.

bash
python main.py -t
bash
python main.py -t -n

With the tests passing, start the assistant. The default invocation reads env.py.

bash
python main.py

The parameter list in the README is short: -t tests configuration, -t -n also tests notifications, and -c points at a custom config file location.

Docker and docker-compose deployment

The Docker path is documented as a three-step sequence. First pull the default config out of the image so you have a local file to edit.

bash
docker run --rm pjialin/py12306 cat /config/env.py > env.py

The README gives an alternative: fetch env.docker.py.example directly from the repository with curl. Then run the container, mounting the current directory at /config and a named volume at /data, and publishing port 8008 for the web console.

bash
docker run --rm --name py12306 -p 8008:8008 -d -v $(pwd):/config -v py12306:/data pjialin/py12306

A 12306.log file appears in the current directory; the README suggests tail -f 12306.log to follow it. The Dockerfile itself creates /data/query and /data/user and declares /data as a volume, and its CMD runs python main.py -c /config/env.py, which is why the config mount is at /config rather than the working directory.

For compose, copy the example file and bring it up. The README shows this without a file argument, so it relies on the default docker-compose.yml name.

bash
cp docker-compose.yml.example docker-compose.yml
bash
docker-compose up -d

One practical detail: the image is built from python:3.6.6-slim, an interpreter line that is long out of support upstream. Rebuilding the image today means either accepting that base or changing the FROM line and re-testing the pinned dependencies yourself.

Where py12306 breaks down

The captcha dependency is the weakest link. The README states plainly that Ruokuai has stopped serving and that free mode is the only remaining option. Free mode routes through a shared community platform, which means the reliability of your order flow depends on a service you do not control and cannot inspect from this repository. If that platform is unavailable or changes its interface, automatic ordering stops working, and the README does not document a fallback.

The dependency set is another constraint. requirements.txt pins exact versions of requests, urllib3, lxml, Flask, Werkzeug and others, including a Jinja2 2.10 and MarkupSafe 1.1.0 pairing that predates the Flask 3.x line listed in the same file. Installing this on a modern Python interpreter is not a supported path; the README only claims Python 3.6 and above, and the Dockerfile settles on 3.6.6. Anyone upgrading Python is on their own.

There is also a compliance boundary the README does not discuss. The project automates ordering against a live ticketing system, and the anti-blocking note concedes that query traffic from major cloud providers gets rate-limited. That is a signal about how the upstream service treats this traffic. Whether running it fits your situation is a judgement the repository leaves entirely to you.

Finally, the release history is thin. The most recent tagged release is v1.0.0 from 2019-03-11, while the last push to the default branch was on 2026-09-17. Development happens on master without versioned releases, so there is no changelog to diff against between upgrades.

How py12306 differs from easy12306 and testerSunshine/12306

The README credits two other projects. It thanks testerSunshine/12306 for implementations it borrowed, and zhaipro/easy12306 for the local captcha recognition model and algorithm.

The difference in approach is worth stating. easy12306 is a local captcha recognition project: its contribution here is the model and algorithm that py12306 can call, so it solves the identification problem rather than the scheduling problem. testerSunshine/12306 is closer in scope, and py12306's own README frames the relationship as reuse rather than replacement.

What py12306 adds on top is operational: a Redis-backed cluster with master election and live config propagation, a Flask console on port 8008 for users, tasks and logs, and a notification layer spanning email, voice call, WeChat, DingTalk and Telegram. If you only need captcha recognition, pulling in the recognition model directly is a smaller dependency than running the whole assistant. If you need a query loop that survives a machine restart and a console to watch it, the cluster and web pieces are the reason to pick this one.

Licence and the cost of staying current

py12306 is released under the Apache License, version 2.0, with the full text in LICENSE at the repository root. Apache-2.0 permits commercial and private use and includes a patent grant, but it also requires that you preserve copyright and licence notices and state significant changes. This is a description of the licence text, not legal advice; if you plan to redistribute a modified version, read the licence and, where it matters, consult a lawyer.

The maintenance cost is real and mostly front-loaded. Because there are no releases after v1.0.0, upgrading means tracking master and reading commits. The pinned dependency set means a Python upgrade is a project, not a command. The captcha path can break without warning from this repository's perspective, since it depends on an external platform. And the notification integrations each have their own credentials and, in the voice case, a purchased appcode from a third-party marketplace. Budget time for the initial configuration and for periodic re-verification with python main.py -t rather than for routine updates.

Editorial conclusion

py12306 suits users who can read Python, keep a config file current, and accept that captcha handling and notification channels depend on third-party services that may change without notice. It is a poor fit for anyone wanting a managed service, or for anyone unwilling to run it from a network where the 12306 query endpoints are not rate-limited. Before committing, verify three things: that python main.py -t passes your account and passenger checks, that your chosen captcha mode is still listed as working in env.py, and that WEB_ENABLE and CLUSTER_ENABLED match the deployment you actually want.

Frequently asked questions

What Python version does py12306 need?

The README states py12306 runs on Python 3.6 or above and that other versions have not been tested. The Dockerfile pins python:3.6.6-slim, which is the only interpreter version the repository explicitly fixes.

How do I check my py12306 configuration before running it?

Run python main.py -t to test configuration, including account, passenger and station detection. Add the -n flag, as in python main.py -t -n, to also send a test notification by voice or email.

Does py12306 support automatic captcha solving?

It does, but the README says the Ruokuai service has stopped and only free mode remains available. Free mode is connected to the shared platform at https://py12306-helper.pjialin.com.

How do I enable the py12306 web management page?

Turn on WEB_ENABLE in the configuration, then start the program and open the host address with the port, which defaults to 8008, for example http://127.0.0.1:8008. The README says the page currently covers users, tasks and live logs.

What does py12306 need for distributed cluster mode?

Cluster mode depends on Redis. Enable CLUSTER_ENABLED in the configuration, and for a separate slave node copy env.slave.py.example to env.slave.py and start it with python main.py -c env.slave.py.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. pjialin/py12306 on GitHub
  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/pjialin-py12306.svg)](https://hysenlabs.com/projects/pjialin-py12306)