Reverse engineered Windows Copilot into an OpenAI-compatible API. Access GPT-4 and GPT-5 models through a simple REST interface without API keys or billing.
Python
1,254
31 commits
updated Jun 27, 2026

Using your own Microsoft Copilot account. No API key, no credits, no paid plan: it turns the free chat at copilot.microsoft.com into an API you can call from code.
You can use it in two ways:
client.chat("Hi"). Supports streaming and multi-turn conversations.http://localhost:8000/v1 that speaks the OpenAI format, so the official openai SDK (and any OpenAI-compatible app) works as a drop-in, with localhost in place of OpenAI.You sign in once in a browser with your Microsoft or Google account; your session is saved and refreshed automatically after that.
Unofficial project. Not affiliated with or endorsed by Microsoft. It automates the consumer Copilot web experience for personal use, so use it responsibly and within Microsoft's terms.
localhost and it just works.conversation_id.# 1. Clone the project
git clone <your-repo-url>
cd Windows-Copilot-API
2. Create and activate a virtual environment
On macOS / Linux:
python3 -m venv venv
source venv/bin/activate
On Windows (PowerShell):
python -m venv venv
venv\Scripts\Activate.ps1
On Windows you may need to allow script execution once:
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned. Incmd.exeactivate withvenv\Scripts\activate.batinstead.
3. Install dependencies and sign in
# Install dependencies
pip install -r requirements.txt
# Install the browser Playwright needs (one-time)
playwright install chromium
# Sign in once: a browser opens, log into your Microsoft or Google account
python -m copilot login
The browser closes by itself once sign-in is detected โ you don't need to press Enter or close it manually. After sign-in it sends one short warm-up message that mints the chat token and passes Cloudflare's "verify you're human" check in the same step (a brief "finishing setupโฆ" appears, and a tiny throwaway chat lands in your history). If a checkbox shows up, click it in that login window. The steps are logged to session/login.log if anything goes wrong. That's it: your session is saved under session/ (git-ignored, never shared) and reused on every run โ so your first request works right away.
๐ ๏ธ Run into trouble during setup or your first run? Head to the Troubleshooting section, the bundled diagnostic both fixes common issues (captcha/clearance) and logs a shareable report.
Prefer a container? You can run the OpenAI-compatible server in Docker once you've signed in.
Sign in on the host first. The login step above opens a visible browser, which can't run inside the headless container โ so run
python -m copilot loginon your host to populatesession/. The container mounts that folder and reuses the Cloudflare clearance earned on the host. It refreshes the chat token headlessly, but it can't earn fresh clearance without a visible browser, so when clearance expires (~30 min) it returns a503โ re-runpython -m copilot loginon the host to refreshsession/.
docker compose up --build
# -> Copilot OpenAI-compatible API on http://localhost:8000
The docker-compose.yml maps port 8000 and bind-mounts your session/ so the login persists across restarts. Tune RATE_LIMIT_RPM / RATE_LIMIT_BURST there. To run without Compose, build and pass the same bindings by hand:
docker build -t windows-copilot-api .
docker run --rm -p 8000:8000 -v "$(pwd)/session:/app/session" windows-copilot-api
The simplest way if your code is already Python.
from copilot import CopilotClient
client = CopilotClient() # loads your signed-in session
# Get a full reply
reply = client.chat("Say hello in one short sentence.")
print(reply.text)
# Continue the SAME conversation โ pass the id back
reply2 = client.chat("And now in French?", reply.conversation_id)
print(reply2.text)
# Stream the answer as it's typed
for chunk in client.stream("Tell me a short joke"):
print(chunk, end="", flush=True)
chat() returns the full text plus a conversation_id; pass that id back to keep the thread going, or omit it to start fresh. stream() yields the reply piece by piece.
๐ More: examples/01_direct_chat.py, 02_direct_conversation.py, 03_direct_stream.py
Start a local server that speaks the OpenAI API, so existing OpenAI tools and SDKs work unchanged.
python app.py
# -> Copilot OpenAI-compatible API on http://127.0.0.1:8000
Then point any OpenAI client at it (the API key is required by the SDK but ignored):
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="unused")
resp = client.chat.completions.create(
model="copilot",
messages=[{"role": "user", "content": "Hello!"}],
)
print(resp.choices[0].message.content)
Or call it with plain HTTP / curl:
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "Hello!"}]}'
Endpoints
| Method | Path | Description |
|---|---|---|
POST | /v1/chat/completions | Chat (supports "stream": true and an optional "conversation_id") |
GET | /v1/models | Lists the single copilot model |
Change the address with env vars:
HOST=0.0.0.0 PORT=8080 python app.py, or runuvicorn server.api:app --host 0.0.0.0 --port 8080.
๐ More: examples/04_server_http.py, 05_server_stream.py, 06_server_openai_sdk.py
python -m copilot login # sign in and save the session
python -m copilot ask "Hello!" # quick one-shot question
Copilot's chat sits behind Cloudflare. Access needs a cf_clearance cookie,
earned by passing a "verify you're human" check in a real browser, and it lasts
about half an hour. The bridge handles this for you:
python -m copilot login earns clearance as part of the same
warm-up that mints your token, so your first request works immediately. If
Cloudflare shows a checkbox, click it in the login window.[copilot] clearance: โฆ progress log, then the answer.On a trusted connection the check often passes invisibly with no window at all. A datacenter/VPN IP is stricter and more likely to show the checkbox; a residential connection clears most reliably.
The server never opens a window: when clearance expires it returns a 503
(type: "clearance_required"). Re-clear out of band with python -m copilot login, then retry.
The server bridges a single signed-in Copilot account, and Copilot's chat socket doesn't tolerate concurrent conversations from one process. So the server serializes upstream calls: parallel HTTP requests queue behind a lock and run one at a time (see server/api.py). This is intentional, and it means throughput is sequential, not parallel.
You can measure where it breaks with the included stress test, which fires a batch of simultaneous requests and doubles the batch size every successful round until the first error:
# Start the server in one terminal
python app.py
# Ramp concurrency in another (1 โ 2 โ 4 โ 8 โ โฆ)
python tests/stress.py
python tests/stress.py --max 64 --timeout 120 --url http://localhost:8000
Sample run (one signed-in account):
| Concurrency | Result | Wall time | Latency (min / median / max) |
|---|---|---|---|
| 1 | โ all ok | 3.7s | 3.7 / 3.7 / 3.7s |
| 2 | โ all ok | 4.6s | 3.4 / 4.6 / 4.6s |
| 4 | โ all ok | 8.3s | 3.7 / 6.7 / 8.3s |
| 8 | โ 1 failed (HTTP 502) | 13.3s | 3.5 / 9.7 / 13.3s |
Highest fully-successful concurrency: 4. Wall time roughly doubles each round
while minimum latency stays flat (~3.5s) โ the signature of a serialized queue:
one request runs immediately, the rest wait their turn. The failure at 8 is an
upstream 502 (Copilot rejecting requests under load), not a server crash or
timeout โ so the exact break point is flaky and may vary between runs.
Takeaway: keep concurrent in-flight requests low (โ 1โ4). This is a personal bridge, not a high-throughput gateway โ and please don't hammer your account.
Concurrency (above) is how many at once; the rate limit is how many per
minute, sustained. Microsoft publishes none for consumer Copilot, so the bridge
enforces a self-imposed one with a token bucket: it caps
accepted requests per minute and returns a standard 429 + Retry-After when
you exceed it. Two env vars tune it:
| Env var | Default | Meaning |
|---|---|---|
RATE_LIMIT_RPM | 12 | Requests/minute the bridge accepts. 0 disables the limit. |
RATE_LIMIT_BURST | 4 | How many requests may go back-to-back before pacing kicks in. |
RATE_LIMIT_RPM=20 RATE_LIMIT_BURST=5 python app.py # raise it; 0 to disable
The default 12 rpm sits safely below the ~15 rpm where a single account starts
seeing upstream 502s. To find your ceiling, run the server with the limiter
off (RATE_LIMIT_RPM=0) and push the probe until failures appear:
python tests/ratelimit.py --rpm 20 --minutes 3
On the client side, use exponential backoff. Both 429 (bridge limit) and
the occasional 502 (Copilot upstream hiccup) are transient โ retry with
growing delays (e.g. 1s, 2s, 4s) and they almost always clear. The official
openai SDK does this automatically and honours Retry-After; with plain HTTP,
add a few retries yourself.
| Path | What it does |
|---|---|
| copilot/ | The core library: CopilotClient, auth, browser sign-in, HTTP driver |
| server/ | The FastAPI OpenAI-compatible server |
| examples/ | Runnable examples for every feature (examples/README.md) |
| tests/ | Test scripts: the concurrency stress test (tests/stress.py) and the diagnostic & report tool (tests/diagnostic.py) |
| app.py | Starts the server |
copilot.session/ (cookies + token) stays on your machine and is git-ignored.Cloudflare clearance is handled automatically (see above), so most "verify you're human" issues clear themselves. If a request still fails, run the diagnostic โ it refreshes the session and writes a shareable report.
python tests/diagnostic.py # browser capture + report
python tests/diagnostic.py --report-only # headless/VPS: report only, no browser
The default run opens your signed-in browser and asks you to send one short message. That single action:
session/profile/ the bridge uses, so passing any "verify you're human" check
earns a fresh cf_clearance cookie, then snapshots the session (cookies +
token) into session/token.json for the pure-HTTP driver to adopt.session/ws_capture.log. A clean turn goes
setOptions โ send โ appendTextโฆ โ done; a {"event":"challenge", "method":"cloudflare",โฆ} frame means Cloudflare gated the turn.It also writes session/diagnostic_report.txt โ environment, the shape of your
session (cookie names + token length, never the values), a live chat probe, and
redacted log tails. Both files are safe to share: access tokens, cookies,
OAuth codes, and emails are redacted before anything is written. Attach
diagnostic_report.txt to a GitHub issue (skim it first) and the cause is
usually obvious.
On a headless server/VPS you can't open a browser, so clearance can't be earned there โ pass
--report-only, and do the clearance step on a machine with a display (or route traffic through a residential connection, e.g. a home-PC exit node), since datacenter IPs are where Cloudflare is strictest.
Need a hand getting this running? Open a GitHub issue for bugs (for setup/auth problems, attach the redacted diagnostic_report.txt from python tests/diagnostic.py), start a discussion to share ideas, or send a pull request.
And if you're working on something interesting, or looking for someone to build it, I'm always open to a chat. Feel free to reach out:
sum_s_sReleased under the MIT License. As this is an unofficial project, you remain responsible for complying with Microsoft's terms of service.
187 followers ยท starred Jun 2026
555 followers ยท starred Jun 2026
86 followers ยท starred Jun 2026
27 followers ยท starred Jun 2026
Reverse engineered Windows Copilot into an OpenAI-compatible API. Access GPT-4 and GPT-5 models through a simple REST interface without API keys or billing.
Python
1,254
31 commits
updated Jun 27, 2026

Using your own Microsoft Copilot account. No API key, no credits, no paid plan: it turns the free chat at copilot.microsoft.com into an API you can call from code.
You can use it in two ways:
client.chat("Hi"). Supports streaming and multi-turn conversations.http://localhost:8000/v1 that speaks the OpenAI format, so the official openai SDK (and any OpenAI-compatible app) works as a drop-in, with localhost in place of OpenAI.You sign in once in a browser with your Microsoft or Google account; your session is saved and refreshed automatically after that.
Unofficial project. Not affiliated with or endorsed by Microsoft. It automates the consumer Copilot web experience for personal use, so use it responsibly and within Microsoft's terms.
localhost and it just works.conversation_id.# 1. Clone the project
git clone <your-repo-url>
cd Windows-Copilot-API
2. Create and activate a virtual environment
On macOS / Linux:
python3 -m venv venv
source venv/bin/activate
On Windows (PowerShell):
python -m venv venv
venv\Scripts\Activate.ps1
On Windows you may need to allow script execution once:
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned. Incmd.exeactivate withvenv\Scripts\activate.batinstead.
3. Install dependencies and sign in
# Install dependencies
pip install -r requirements.txt
# Install the browser Playwright needs (one-time)
playwright install chromium
# Sign in once: a browser opens, log into your Microsoft or Google account
python -m copilot login
The browser closes by itself once sign-in is detected โ you don't need to press Enter or close it manually. After sign-in it sends one short warm-up message that mints the chat token and passes Cloudflare's "verify you're human" check in the same step (a brief "finishing setupโฆ" appears, and a tiny throwaway chat lands in your history). If a checkbox shows up, click it in that login window. The steps are logged to session/login.log if anything goes wrong. That's it: your session is saved under session/ (git-ignored, never shared) and reused on every run โ so your first request works right away.
๐ ๏ธ Run into trouble during setup or your first run? Head to the Troubleshooting section, the bundled diagnostic both fixes common issues (captcha/clearance) and logs a shareable report.
Prefer a container? You can run the OpenAI-compatible server in Docker once you've signed in.
Sign in on the host first. The login step above opens a visible browser, which can't run inside the headless container โ so run
python -m copilot loginon your host to populatesession/. The container mounts that folder and reuses the Cloudflare clearance earned on the host. It refreshes the chat token headlessly, but it can't earn fresh clearance without a visible browser, so when clearance expires (~30 min) it returns a503โ re-runpython -m copilot loginon the host to refreshsession/.
docker compose up --build
# -> Copilot OpenAI-compatible API on http://localhost:8000
The docker-compose.yml maps port 8000 and bind-mounts your session/ so the login persists across restarts. Tune RATE_LIMIT_RPM / RATE_LIMIT_BURST there. To run without Compose, build and pass the same bindings by hand:
docker build -t windows-copilot-api .
docker run --rm -p 8000:8000 -v "$(pwd)/session:/app/session" windows-copilot-api
The simplest way if your code is already Python.
from copilot import CopilotClient
client = CopilotClient() # loads your signed-in session
# Get a full reply
reply = client.chat("Say hello in one short sentence.")
print(reply.text)
# Continue the SAME conversation โ pass the id back
reply2 = client.chat("And now in French?", reply.conversation_id)
print(reply2.text)
# Stream the answer as it's typed
for chunk in client.stream("Tell me a short joke"):
print(chunk, end="", flush=True)
chat() returns the full text plus a conversation_id; pass that id back to keep the thread going, or omit it to start fresh. stream() yields the reply piece by piece.
๐ More: examples/01_direct_chat.py, 02_direct_conversation.py, 03_direct_stream.py
Start a local server that speaks the OpenAI API, so existing OpenAI tools and SDKs work unchanged.
python app.py
# -> Copilot OpenAI-compatible API on http://127.0.0.1:8000
Then point any OpenAI client at it (the API key is required by the SDK but ignored):
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="unused")
resp = client.chat.completions.create(
model="copilot",
messages=[{"role": "user", "content": "Hello!"}],
)
print(resp.choices[0].message.content)
Or call it with plain HTTP / curl:
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "Hello!"}]}'
Endpoints
| Method | Path | Description |
|---|---|---|
POST | /v1/chat/completions | Chat (supports "stream": true and an optional "conversation_id") |
GET | /v1/models | Lists the single copilot model |
Change the address with env vars:
HOST=0.0.0.0 PORT=8080 python app.py, or runuvicorn server.api:app --host 0.0.0.0 --port 8080.
๐ More: examples/04_server_http.py, 05_server_stream.py, 06_server_openai_sdk.py
python -m copilot login # sign in and save the session
python -m copilot ask "Hello!" # quick one-shot question
Copilot's chat sits behind Cloudflare. Access needs a cf_clearance cookie,
earned by passing a "verify you're human" check in a real browser, and it lasts
about half an hour. The bridge handles this for you:
python -m copilot login earns clearance as part of the same
warm-up that mints your token, so your first request works immediately. If
Cloudflare shows a checkbox, click it in the login window.[copilot] clearance: โฆ progress log, then the answer.On a trusted connection the check often passes invisibly with no window at all. A datacenter/VPN IP is stricter and more likely to show the checkbox; a residential connection clears most reliably.
The server never opens a window: when clearance expires it returns a 503
(type: "clearance_required"). Re-clear out of band with python -m copilot login, then retry.
The server bridges a single signed-in Copilot account, and Copilot's chat socket doesn't tolerate concurrent conversations from one process. So the server serializes upstream calls: parallel HTTP requests queue behind a lock and run one at a time (see server/api.py). This is intentional, and it means throughput is sequential, not parallel.
You can measure where it breaks with the included stress test, which fires a batch of simultaneous requests and doubles the batch size every successful round until the first error:
# Start the server in one terminal
python app.py
# Ramp concurrency in another (1 โ 2 โ 4 โ 8 โ โฆ)
python tests/stress.py
python tests/stress.py --max 64 --timeout 120 --url http://localhost:8000
Sample run (one signed-in account):
| Concurrency | Result | Wall time | Latency (min / median / max) |
|---|---|---|---|
| 1 | โ all ok | 3.7s | 3.7 / 3.7 / 3.7s |
| 2 | โ all ok | 4.6s | 3.4 / 4.6 / 4.6s |
| 4 | โ all ok | 8.3s | 3.7 / 6.7 / 8.3s |
| 8 | โ 1 failed (HTTP 502) | 13.3s | 3.5 / 9.7 / 13.3s |
Highest fully-successful concurrency: 4. Wall time roughly doubles each round
while minimum latency stays flat (~3.5s) โ the signature of a serialized queue:
one request runs immediately, the rest wait their turn. The failure at 8 is an
upstream 502 (Copilot rejecting requests under load), not a server crash or
timeout โ so the exact break point is flaky and may vary between runs.
Takeaway: keep concurrent in-flight requests low (โ 1โ4). This is a personal bridge, not a high-throughput gateway โ and please don't hammer your account.
Concurrency (above) is how many at once; the rate limit is how many per
minute, sustained. Microsoft publishes none for consumer Copilot, so the bridge
enforces a self-imposed one with a token bucket: it caps
accepted requests per minute and returns a standard 429 + Retry-After when
you exceed it. Two env vars tune it:
| Env var | Default | Meaning |
|---|---|---|
RATE_LIMIT_RPM | 12 | Requests/minute the bridge accepts. 0 disables the limit. |
RATE_LIMIT_BURST | 4 | How many requests may go back-to-back before pacing kicks in. |
RATE_LIMIT_RPM=20 RATE_LIMIT_BURST=5 python app.py # raise it; 0 to disable
The default 12 rpm sits safely below the ~15 rpm where a single account starts
seeing upstream 502s. To find your ceiling, run the server with the limiter
off (RATE_LIMIT_RPM=0) and push the probe until failures appear:
python tests/ratelimit.py --rpm 20 --minutes 3
On the client side, use exponential backoff. Both 429 (bridge limit) and
the occasional 502 (Copilot upstream hiccup) are transient โ retry with
growing delays (e.g. 1s, 2s, 4s) and they almost always clear. The official
openai SDK does this automatically and honours Retry-After; with plain HTTP,
add a few retries yourself.
| Path | What it does |
|---|---|
| copilot/ | The core library: CopilotClient, auth, browser sign-in, HTTP driver |
| server/ | The FastAPI OpenAI-compatible server |
| examples/ | Runnable examples for every feature (examples/README.md) |
| tests/ | Test scripts: the concurrency stress test (tests/stress.py) and the diagnostic & report tool (tests/diagnostic.py) |
| app.py | Starts the server |
copilot.session/ (cookies + token) stays on your machine and is git-ignored.Cloudflare clearance is handled automatically (see above), so most "verify you're human" issues clear themselves. If a request still fails, run the diagnostic โ it refreshes the session and writes a shareable report.
python tests/diagnostic.py # browser capture + report
python tests/diagnostic.py --report-only # headless/VPS: report only, no browser
The default run opens your signed-in browser and asks you to send one short message. That single action:
session/profile/ the bridge uses, so passing any "verify you're human" check
earns a fresh cf_clearance cookie, then snapshots the session (cookies +
token) into session/token.json for the pure-HTTP driver to adopt.session/ws_capture.log. A clean turn goes
setOptions โ send โ appendTextโฆ โ done; a {"event":"challenge", "method":"cloudflare",โฆ} frame means Cloudflare gated the turn.It also writes session/diagnostic_report.txt โ environment, the shape of your
session (cookie names + token length, never the values), a live chat probe, and
redacted log tails. Both files are safe to share: access tokens, cookies,
OAuth codes, and emails are redacted before anything is written. Attach
diagnostic_report.txt to a GitHub issue (skim it first) and the cause is
usually obvious.
On a headless server/VPS you can't open a browser, so clearance can't be earned there โ pass
--report-only, and do the clearance step on a machine with a display (or route traffic through a residential connection, e.g. a home-PC exit node), since datacenter IPs are where Cloudflare is strictest.
Need a hand getting this running? Open a GitHub issue for bugs (for setup/auth problems, attach the redacted diagnostic_report.txt from python tests/diagnostic.py), start a discussion to share ideas, or send a pull request.
And if you're working on something interesting, or looking for someone to build it, I'm always open to a chat. Feel free to reach out:
sum_s_sReleased under the MIT License. As this is an unofficial project, you remain responsible for complying with Microsoft's terms of service.
187 followers ยท starred Jun 2026
555 followers ยท starred Jun 2026
86 followers ยท starred Jun 2026
27 followers ยท starred Jun 2026