{"id":41527378,"url":"https://github.com/sarperavci/uaforge","last_synced_at":"2026-05-21T15:01:22.440Z","repository":{"id":328324152,"uuid":"1115110907","full_name":"sarperavci/UAForge","owner":"sarperavci","description":"Generate statistically accurate User Agents and Client Hints (Sec-CH-UA). Deterministic, data-driven browser identities based on real-world market share distributions.","archived":false,"fork":false,"pushed_at":"2026-05-17T04:09:31.000Z","size":130,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-17T06:34:56.706Z","etag":null,"topics":["automation","browser-fingerprinting","client-hints","data-driven","python","sec-ch-ua-spoofing","testing","user-agent","web-scraping"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sarperavci.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-12T11:01:15.000Z","updated_at":"2026-05-17T04:09:34.000Z","dependencies_parsed_at":"2026-03-15T07:01:42.892Z","dependency_job_id":null,"html_url":"https://github.com/sarperavci/UAForge","commit_stats":null,"previous_names":["sarperavci/uaforge"],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/sarperavci/UAForge","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sarperavci%2FUAForge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sarperavci%2FUAForge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sarperavci%2FUAForge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sarperavci%2FUAForge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sarperavci","download_url":"https://codeload.github.com/sarperavci/UAForge/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sarperavci%2FUAForge/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33305277,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-21T12:23:38.849Z","status":"ssl_error","status_checked_at":"2026-05-21T12:22:11.673Z","response_time":62,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automation","browser-fingerprinting","client-hints","data-driven","python","sec-ch-ua-spoofing","testing","user-agent","web-scraping"],"created_at":"2026-01-23T22:23:49.578Z","updated_at":"2026-05-21T15:01:22.423Z","avatar_url":"https://github.com/sarperavci.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# UAForge\n\n**Enterprise-grade, deterministic User Agent \u0026 Client Hint generator based on real-world browser statistics**\n\n[![Python Version](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)\n[![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)\n[![Auto-Updated](https://img.shields.io/badge/data-auto--updated%20weekly-orange)](https://caniuse.com/usage-table)\n\n---\n\nMost \"random user-agent\" libraries are broken. They generate outdated browser strings, mix incompatible OS combinations, or lack the modern headers that fingerprinting systems now check.\n\n**UAForge takes a different approach.** Instead of picking random strings, it simulates real users based on **statistical probability**. If Chrome 143 on Android holds 40% global market share, UAForge generates that identity 40% of the time.\n\nIt also generates matching **Client Hints (`Sec-CH-UA`)** headers automatically—allowing your automation to pass modern fingerprinting checks that go beyond the legacy User-Agent string.\n\n### Key Features\n\n*   **Statistically Accurate** — Weighted by real-world global usage data, updated weekly from caniuse.com\n*   **Smart Correlations** — Enforces valid browser↔OS mappings (no Safari on Windows)\n*    **Real Hardware** — Injects actual device models (Pixel 9, Galaxy S24, etc.) for mobile agents\n*   **Client Hints** — Generates Sec-CH-UA, Sec-CH-UA-Mobile, Sec-CH-UA-Platform, and GREASE tokens\n*    **Deterministic** — Seed support for consistent, reproducible identities across sessions\n\n\n## Installation\n\n```bash\npip install git+https://github.com/sarperavci/uaforge.git\n```\n\n*Note:* The repo does not contain the market-share data files due to size constraints. They are automatically downloaded from the release assets while installing.\n\n\n## Quick Start\n\nThe API is designed to be simple. You generate an \"Identity\" object, which contains everything you need for your requests.\n\n```python\nfrom uaforge.core.generator import UserAgentGenerator\n\n# 1. Initialize the generator\nagent = UserAgentGenerator()\n\n# 2. Generate an identity\nidentity = agent.generate()\n\n# 3. Get the headers (includes User-Agent AND Client Hints)\nheaders = identity.get_headers()\nfull_client_hints = identity.get_all_client_hints()\n\n# Use with requests/httpx\n# response = requests.get(\"https://httpbin.org/headers\", headers=headers)\n\nprint(f\"Browser: {identity.meta_browser.value}\")\nprint(f\"OS:      {identity.meta_os.value}\")\nprint(headers)\nprint(full_client_hints)\n```\n\n### Sample Output\n\nDefault Headers:\n\n```json\n{\n\"User-Agent\": \"Mozilla/5.0 (Linux; Android 13; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Mobile Safari/537.36\",\n\"Sec-CH-UA\": \"\\\"Not A Brand\\\";v=\\\"99\\\", \\\"Chromium\\\";v=\\\"144\\\", \\\"Google Chrome\\\";v=\\\"144\\\"\",\n\"Sec-CH-UA-Mobile\": \"?1\",\n\"Sec-CH-UA-Platform\": \"\\\"Android\\\"\"\n}\n```\n\nFull Client Hints:\n\n```json\n{\n\"Sec-CH-UA\": \"\\\"Not A Brand\\\";v=\\\"99\\\", \\\"Chromium\\\";v=\\\"144\\\", \\\"Google Chrome\\\";v=\\\"144\\\"\",\n\"Sec-CH-UA-Mobile\": \"?1\",\n\"Sec-CH-UA-Platform\": \"\\\"Android\\\"\",\n\"Sec-CH-UA-Full-Version-List\": \"\\\"Not A Brand\\\";v=\\\"99.0.0.0\\\", \\\"Chromium\\\";v=\\\"144.0.7559.59\\\", \\\"Google Chrome\\\";v=\\\"144.0.7559.59\\\"\",\n\"Sec-CH-UA-Platform-Version\": \"13.0.0\",\n\"Sec-CH-UA-Model\": \"\\\"Pixel 9\\\"\",\n\"Sec-CH-UA-Arch\": \"\\\"arm\\\"\",\n\"Sec-CH-UA-Bitness\": \"\\\"64\\\"\",\n\"Sec-CH-UA-Full-Version\": \"\\\"144.0.7559.59\\\"\",\n\"Sec-CH-UA-Form-Factors\": \"\\\"Mobile\\\"\",\n\"Sec-CH-UA-WoW64\": \"?0\",\n\"Sec-CH-Prefers-Color-Scheme\": \"dark\"\n}\n```\n\n## Advanced Usage\n\n### Deterministic Generation (Sessions)\nIf you are managing long-running sessions, you need the User Agent to stay consistent across restarts. Use a seed.\n\n```python\n# The identity generated here will ALWAYS be the same for seed 42\nuser = UserAgentGenerator(seed=42).generate()\n\nprint(user.user_agent) \n# Useful for associating a UA with a specific database UserID\n```\n\n### Accessing Granular Data\nSometimes you need just the OS version or just the device model for analytics.\n\n```python\nidentity = agent.generate()\n\nif identity.meta_device == \"mobile\":\n    print(f\"Device Model: {identity.ch_model}\")  # e.g. \"Pixel 8 Pro\"\n    print(f\"Architecture: {identity.ch_arch}\")   # e.g. \"arm\"\n```\n\n## How it works\n\n### The Data Sources\n\nWe don't guess. We utilize three distinct data layers:\n\n1.  **`market_share.json`**: Global browser usage stats (Chrome, Safari, Edge, Firefox).\n2.  **`os_distribution.json`**: The probability of an OS given a specific browser (e.g., Safari is 100% macOS/iOS, but Chrome is split between Windows, Mac, Linux, and Android).\n3.  **`device_models.json`**: A curated list of ~500 real-world mobile device fingerprints.\n\n## Maintenance \u0026 Updates\n\nThe browser ecosystem moves fast. Market share data is **automatically updated weekly** via GitHub Actions by parsing the latest data from [caniuse.com](https://caniuse.com/usage-table).\n\nYou can also trigger a manual update by running:\n```bash\npython scripts/parse_caniuse.py\n```\n\n## Current Market Share Distribution\n\nThe table below shows the aggregated browser market share from the current dataset. Data is sourced from caniuse.com and updated automatically.\n\n| Browser | Market Share |\n|---------|-------------|\n| Chrome for Android | 57.56% |\n| Chrome (Desktop) | 20.19% |\n| iOS Safari | 11.46% |\n| Edge | 4.22% |\n| Safari (Desktop) | 1.98% |\n| Firefox | 1.34% |\n| Samsung Internet | 1.22% |\n| Opera Mobile | 0.74% |\n| UC Browser | 0.54% |\n| Firefox for Android | 0.30% |\n| IE | 0.24% |\n| Android Browser | 0.21% |\n\n*Last updated: 21-05-2026*\n\n## License\n\nMIT License. Feel free to use this in your commercial scrapers, bots or testing suites.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsarperavci%2Fuaforge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsarperavci%2Fuaforge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsarperavci%2Fuaforge/lists"}