bas
Copy-paste your browser's request, bas handles the rest.
No impersonation needed. No fingerprint chasing. No broken cookies. Just grab your headers + cookies from DevTools and go.
Why bas?
| Problem | curlcffi | bas |
|---|---|---|
| Cookies lost during redirects | BUG | Fixed |
| Cookies not accumulating across requests | BUG | Fixed |
| Manual Cookie header construction | Yes | Auto-injected |
| Requires curl-impersonate | Yes | No — pure Python |
| Browser fingerprint updates | Constant chase | None — bring your own headers |
| External dependencies | Yes | Zero |
Installation
pip install bas-http
Quick Start
Method 1: Paste a curl command from DevTools (Recommended)
This is the fastest way. Copy a curl command from your browser and bas does the rest.
1. Open browser DevTools (F12) → Network tab
2. Make a request on the website
3. Right-click the request → "Copy as cURL"
4. Paste into Python
import bas
# Paste your curl command (use r'' raw string to preserve backslashes)
s = bas.from_curl(r'''curl "https://example.com/page" ^
-H "accept: text/html,application/xhtml+xml" ^
-H "accept-language: en-US,en;q=0.9" ^
-H "user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36" ^
-b "cf_clearance=abc123; session_id=xyz; token=def456"''')
# Now make requests — all headers and cookies are auto-injected
r = s.get("https://example.com/page")
print(r.status_code)
print(r.text)
# Follow-up requests keep the same headers and cookies
r2 = s.get("https://example.com/dashboard")
print(r2.status_code)
# Add more cookies manually if needed
s.set_cookie("new_cookie", "value", domain="example.com")
r3 = s.get("https://example.com/api/data")
Method 2: Build headers manually
If you have headers copied from DevTools (not as a curl command):
import bas
s = bas.Session()
# Paste your headers from DevTools → Network → Headers tab
s.headers = {
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
"accept-language": "en-US,en;q=0.9",
"cache-control": "max-age=0",
"sec-ch-ua": '"Chromium";v="137", "Not/A)Brand";v="24"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
"sec-fetch-dest": "document",
"sec-fetch-mode": "navigate",
"sec-fetch-site": "none",
"sec-fetch-user": "?1",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36",
}
# Add your cookies from DevTools → Application → Cookies
s.set_cookie("cf_clearance", "abc123", domain="example.com")
s.set_cookie("session_id", "xyz", domain="example.com")
s.set_cookie("csrf_token", "def456", domain="example.com")
# Go!
r = s.get("https://example.com/page")
print(r.status_code, len(r.text))
Method 3: Headers + cookies in one call
import bas
s = bas.from_headers(
url="https://example.com",
headers={
"user-agent": "Mozilla/5.0 ...",
"accept": "text/html,...",
},
cookies={
"cf_clearance": "abc123",
"session": "xyz",
},
)
r = s.get("https://example.com/page")
HTTP Methods
import bas
s = bas.from_curl(r'''curl "https://example.com" -b "session=abc"''')
# GET
r = s.get("https://example.com/page")
# POST with form data
r = s.post("https://example.com/login", data={"username": "user", "password": "pass"})
# POST with JSON
r = s.post("https://example.com/api", json={"key": "value"})
# PUT
r = s.put("https://example.com/api/123", json={"name": "updated"})
# DELETE
r = s.delete("https://example.com/api/123")
# PATCH
r = s.patch("https://example.com/api/123", json={"name": "patched"})
# HEAD
r = s.head("https://example.com/page")
Response Object
r = s.get("https://example.com/page")
# Status code
print(r.status_code) # 200
print(r.ok) # True (status < 400)
print(r.reason_phrase) # "OK"
# Content
print(r.text) # Decoded text
print(r.body) # Raw bytes
print(r.json) # Parsed JSON
print(r.content_type) # "text/html; charset=utf-8"
# Headers
print(r.headers) # Case-insensitive headers dict
print(r.headers["content-type"])
# URL info
print(r.url) # Final URL (after redirects)
print(r.ip) # Server IP
print(r.http_version) # "1.1" or "2.0"
print(r.elapsed) # Response time in seconds
# Redirect history
for resp in r.history:
print(resp.status_code, resp.url)
# Raise exception on error
r.raise_for_status() # Raises HTTPError if status >= 400
# Iterate over content
for chunk in r.iter_content(chunk_size=8192):
process(chunk)
for line in r.iter_lines():
print(line)
Cookie Management
Auto-injection
bas automatically injects cookies into every request. No manual header construction needed.
import bas
s = bas.from_curl(r'''curl "https://example.com" -b "session=abc123; token=xyz"''')
# This request automatically has Cookie: session=abc123; token=xyz
r = s.get("https://example.com/page")
# See what cookies were sent
print(r.request.headers.get("Cookie"))
# "session=abc123; token=xyz"
Set cookies manually
s = bas.Session()
s.set_cookie("name", "value", domain="example.com", path="/")
Get cookies for a URL
cookies = s.get_cookies("https://example.com/page")
print(cookies) # {"session": "abc123", "token": "xyz"}
Clear all cookies
s.clear_cookies()
Save cookies to file
# JSON format
s.save_cookies("cookies.json")
# Netscape format (compatible with curl/wget)
s.save_cookies("cookies.txt", format="netscape")
Load cookies from file
s.load_cookies("cookies.json")
s.load_cookies("cookies.txt", format="netscape")
Cookie accumulation across requests
When the server sends Set-Cookie headers, bas automatically stores them:
s = bas.Session()
# Server sets cookies in this response
r1 = s.get("https://example.com/login")
# Set-Cookie: session_id=abc123; Path=/
# Set-Cookie: user=john; Path=/
# These cookies are automatically sent with the next request
r2 = s.get("https://example.com/dashboard")
# Cookie: session_id=abc123; user=john
Cookies survive redirects
# Server redirects and sets more cookies
r = s.get("https://example.com/page")
# 302 → https://example.com/dashboard
# Set-Cookie: tracking=xyz; Path=/
# ALL cookies are preserved through redirects
# No cookies lost (unlike curlcffi!)
Session Persistence
Keep a session alive across multiple script runs:
import bas
s = bas.Session()
# Try to load previous session
try:
s.load_cookies("my_session.json")
except FileNotFoundError:
pass
# Make requests
r = s.get("https://example.com/page")
# Save session for next run
s.save_cookies("my_session.json")
Parse curl commands
from bas.curl_parser import parse_curl, print_curl_summary
# See what's in a curl command
print_curl_summary(r'''curl "https://example.com" -H "User-Agent: ..." -b "cookie=value"''')
# Or get it as a dict
parsed = parse_curl(r'''curl "https://example.com" -H "User-Agent: ..." -b "cookie=value"''')
print(parsed["method"]) # "GET"
print(parsed["url"]) # "https://example.com"
print(parsed["headers"]) # {"User-Agent": "..."}
print(parsed["cookies"]) # {"cookie": "value"}
print(parsed["user_agent"]) # "Mozilla/5.0 ..."
print(parsed["referer"]) # "..."
SSL Verification
# Disable SSL verification (default is enabled)
s = bas.Session(verify=False)
# Or per-request
r = s.get("https://self-signed.example.com", verify=False)
Timeouts
# Set default timeout (seconds)
s = bas.Session(timeout=60)
# Or per-request
r = s.get("https://slow.example.com", timeout=120)
Redirects
# Follow redirects (default: True)
r = s.get("https://example.com/page")
# Don't follow redirects
r = s.get("https://example.com/page", allow_redirects=False)
print(r.status_code) # 302
print(r.location) # "https://example.com/dashboard"
# Set max redirects
s = bas.Session(max_redirects=5)
Proxy Support
# TODO: Proxy support coming in v1.1
Real-World Example: Web Scraping
import bas
# Step 1: Copy curl from DevTools
s = bas.from_curl(r'''curl "https://spaceshooter.net/faucet/ltc" ^
-H "accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" ^
-H "accept-language: en-US,en;q=0.9" ^
-H "sec-ch-ua: \"Chromium\";v=\"137\", \"Not/A)Brand\";v=\"24\"" ^
-H "sec-ch-ua-mobile: ?0" ^
-H "sec-ch-ua-platform: \"Windows\"" ^
-H "sec-fetch-dest: document" ^
-H "sec-fetch-mode: navigate" ^
-H "sec-fetch-site: none" ^
-H "upgrade-insecure-requests: 1" ^
-H "user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36" ^
-b "captcha=rscaptcha; cf_clearance=abc123; ci_session=xyz123; uf=def456"''')
# Step 2: Make the request
r = s.get("https://spaceshooter.net/faucet/ltc")
# Step 3: Parse the response
if r.ok:
print("Success!")
print(f"Status: {r.status_code}")
print(f"Content length: {len(r.text)}")
print(f"Cookies after request: {s.get_cookies('https://spaceshooter.net')}")
else:
print(f"Failed: {r.status_code}")
Real-World Example: Form Submission
import bas
# Copy the POST request curl from DevTools
s = bas.from_curl(r'''curl "https://example.com/login" ^
-H "content-type: application/x-www-form-urlencoded" ^
-H "user-agent: Mozilla/5.0 ..." ^
-H "referer: https://example.com/login" ^
-b "csrf_token=abc123" ^
--data-raw "username=myuser&password=mypass"''')
r = s.post("https://example.com/login", data={
"username": "myuser",
"password": "mypass",
})
print(r.status_code)
Comparison with curlcffi
curlcffi (BROKEN)
from curl_cffi.requests import Session
s = Session(impersonate="chrome131")
# Cookies lost during redirects!
r1 = s.get("https://example.com/page") # 302 redirect
# r1 is missing cookies from Set-Cookie headers!
# Cookies not accumulating!
r2 = s.get("https://example.com/page")
# r2 has LOST cookies from r1!
bas (FIXED)
import bas
s = bas.from_curl(r'''curl "https://example.com" -b "session=abc"''')
# Cookies survive redirects!
r1 = s.get("https://example.com/page") # 302 redirect
# ALL cookies preserved through redirect
# Cookies accumulate!
r2 = s.get("https://example.com/dashboard")
# r2 has ALL cookies from r1 + new ones
# No manual Cookie header needed!
# bas auto-injects cookies from the jar
API Reference
bas.from_curl(curl_cmd, **kwargs) → Session
Create a Session from a curl command. Main entry point.
bas.from_headers(url, headers, cookies, **kwargs) → Session
Create a Session from raw headers and cookies.
bas.Session(headers, cookies, verify, timeout, allow_redirects, max_redirects)
Pure Python HTTP session. Zero external dependencies.
Methods:
get(url, **kwargs)→ Responsepost(url, **kwargs)→ Responseput(url, **kwargs)→ Responsedelete(url, **kwargs)→ Responsepatch(url, **kwargs)→ Responsehead(url, **kwargs)→ Responseset_cookie(name, value, domain, path)→ Noneget_cookies(url)→ dictclear_cookies()→ Nonesave_cookies(filepath, format)→ Noneload_cookies(filepath, format)→ None
bas.Cookie(name, value, domain, path, expires, max_age, secure, http_only, same_site)
Individual cookie object with RFC 6265 compliance.
bas.CookieJar
Thread-safe cookie container. Full RFC 6265 domain/path matching.
Methods:
add(cookie)→ Noneremove(cookie)→ Noneget(name, domain, path)→ Cookie | Nonematch(url)→ list[Cookie]to_header(url)→ strparse_set_cookie(header, url)→ Cookiesave_json(filepath)→ Noneload_json(filepath)→ Nonesave_netscape(filepath)→ Noneload_netscape(filepath)→ None
bas.Response
HTTP response object.
Properties:
status_code(int): HTTP status codeok(bool): True if status < 400text(str): Decoded text contentbody(bytes): Raw bytesjson(Any): Parsed JSONheaders(Headers): Response headersurl(str): Final URLelapsed(float): Response timehistory(list): Redirect historycookies(CookieJar): Cookie jar
License
MIT
Release files for bas-http 1.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 | |
|---|---|---|---|
| bas_http-1.0.2.tar.gz | 46.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bas_http-1.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 100.2 kB
Release files / bas_http-1.0.2.tar.gz
| Download URL | bas_http-1.0.2.tar.gz |
|---|---|
| Size | 46.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
20de0822d5957a48e35f1ecc0ada2cf4dd106362e5cb657ef9680adfde133167
|
|
BLAKE2b-256 checksum How to use checksums |
c70773f47cbfdd59164cf1e944bb826f1f05a954392643ede5fb4c5ce4a3fe0a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.10
|
Release files / bas_http-1.0.2-py3-none-any.whl
| Download URL | bas_http-1.0.2-py3-none-any.whl |
|---|---|
| Size | 53.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d5dda2e039da26224d2f1723da5aec853d37da9943d97792c808e3bd8ca8447f
|
|
BLAKE2b-256 checksum How to use checksums |
3aa3b049928b74c689e7a71c5cbf73eacfc628451bff7dc83d48e5dd30885c9c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.10
|