{"id":41,"library":"linear-python","title":"Linear Python SDK","description":"There is NO official Python SDK for Linear. Linear's official SDK is TypeScript only (@linear/sdk). For Python, three competing community packages exist: linear-py, linear-api, and linear-python — none are official. The recommended approach for production Python use is raw GraphQL over HTTP. Current best community package is linear-api 0.2.0.","status":"active","version":"0.2.0","language":"python","source_language":"en","source_url":"https://developers.linear.app/docs","tags":["linear","project-management","graphql","issues","api","python"],"install":[{"cmd":"pip install linear-api","lang":"bash","label":"Python (best community wrapper — Pydantic models, auto-pagination)"},{"cmd":"pip install linear-py","lang":"bash","label":"Python (minimal community wrapper — limited features)"}],"dependencies":[{"reason":"Required by linear-api for model validation.","package":"pydantic>=2.0","optional":false},{"reason":"HTTP client for raw GraphQL approach.","package":"requests","optional":true}],"imports":[{"note":"linear-api installs as linear_api, not linear. No official Python package named linear exists.","wrong":"from linear import LinearClient","symbol":"LinearClient","correct":"from linear_api import LinearClient"},{"note":"For production use, raw GraphQL is more reliable than any community wrapper. linear-py has very limited feature coverage.","wrong":"from linear_py import Linear","symbol":"Raw GraphQL (recommended)","correct":"import urllib.request  # raw GraphQL over HTTP"}],"quickstart":{"code":"import json\nimport urllib.request\n\nAPI_KEY = 'lin_api_YOUR_KEY'\n\ndef graphql_request(query, variables=None):\n    data = json.dumps({'query': query, 'variables': variables or {}}).encode()\n    req = urllib.request.Request(\n        'https://api.linear.app/graphql',\n        data=data,\n        headers={\n            'Authorization': API_KEY,\n            'Content-Type': 'application/json'\n        }\n    )\n    with urllib.request.urlopen(req) as resp:\n        result = json.loads(resp.read())\n    if 'errors' in result:\n        raise Exception(result['errors'])\n    return result['data']\n\n# Get current user\nme = graphql_request('{ viewer { id name email } }')\nprint(me)\n\n# Get issues for a team\nquery = '''\nquery TeamIssues($teamId: String!) {\n    team(id: $teamId) {\n        issues { nodes { id identifier title state { name } } }\n    }\n}\n'''\nissues = graphql_request(query, {'teamId': 'YOUR_TEAM_ID'})","lang":"python","description":"Raw GraphQL approach — recommended for production Python use with Linear API."},"warnings":[{"fix":"Use raw GraphQL over HTTP for production. Use linear-api for prototyping if you want an ORM-style wrapper.","message":"There is no official Python SDK for Linear. linear-py, linear-api, and linear-python are all community packages with no official support. Linear's official SDK is TypeScript only.","severity":"breaking","affected_versions":"all"},{"fix":"Always check 'if errors in result' before accessing result['data'].","message":"GraphQL queries return 200 OK even on errors. The 'errors' array in the response must be checked explicitly — HTTP status alone does not indicate success.","severity":"breaking","affected_versions":"all"},{"fix":"Implement refresh token flow before April 1 2026. See Linear OAuth developer documentation.","message":"OAuth apps: starting October 1 2025 all newly created OAuth apps issue refresh tokens by default. Existing apps must migrate to refresh tokens by April 1 2026 or OAuth tokens will stop working.","severity":"breaking","affected_versions":"all OAuth integrations"},{"fix":"Use the current user role management mutations in the schema.","message":"userPromoteAdmin, userDemoteAdmin, userPromoteMember, userDemoteMember mutations removed from GraphQL schema. Any code using these will get schema validation errors.","severity":"breaking","affected_versions":"all"},{"fix":"headers = {'Authorization': 'lin_api_YOUR_KEY'} — no Bearer prefix.","message":"Linear API token format is 'lin_api_...' — do not use Bearer prefix. Correct header is Authorization: lin_api_... not Authorization: Bearer lin_api_...","severity":"gotcha","affected_versions":"all"},{"fix":"Filter by number: {'number': {'eq': 123}} then match exact identifier in results.","message":"Issue search by identifier (e.g. ENG-123) requires filtering by number first then matching identifier. Direct filter on identifier string is not supported.","severity":"gotcha","affected_versions":"all"},{"fix":"Use issueUpdate with labelIds array instead.","message":"issueAddLabel mutation does not exist. Common LLM-generated code attempts to call it and gets a 400 error.","severity":"gotcha","affected_versions":"all"},{"fix":"Use linear-api for community wrapper or raw GraphQL.","message":"linear-python (PyPI) is an older abandoned wrapper. Do not confuse with linear-api or linear-py.","severity":"deprecated","affected_versions":"all"}],"env_vars":null,"last_verified":"2026-05-12T05:25:43.427Z","next_check":"2026-04-17T00:00:00.000Z","problems":[{"fix":"Install the package using pip: `pip install linear-python`.","cause":"The 'linear-python' package is not installed in the current Python environment.","error":"ModuleNotFoundError: No module named 'linear_python'"},{"fix":"Ensure your Linear API key is correctly configured as an environment variable (e.g., `LINEAR_API_KEY`) or passed directly to the `LinearClient` constructor, and verify its validity and scopes in your Linear settings.","cause":"The Linear API key or authentication token provided to the 'linear-python' client is invalid, missing, or lacks the necessary permissions.","error":"linear_python.exceptions.LinearAPIError: 401 Unauthorized"},{"fix":"Consult the 'linear-python' library's source code or any available documentation to find the correct method names and usage patterns, or consider using a more actively maintained library like `linear-api`.","cause":"The method or attribute being called (e.g., `create_issue`) does not exist on the `LinearClient` object, or its name is different, indicating an incorrect usage of the 'linear-python' library.","error":"AttributeError: 'LinearClient' object has no attribute 'create_issue'"},{"fix":"Examine the specific error message to identify the missing or invalid field, and adjust your 'linear-python' call to provide the correct data according to the Linear API's GraphQL schema.","cause":"The request made through 'linear-python' to the Linear API is missing a required field or contains data that does not conform to the Linear GraphQL schema.","error":"linear_python.exceptions.LinearAPIError: 400 Bad Request: GraphQL validation error: Field 'title' is required."}],"ecosystem":"pypi","meta_description":null,"install_score":80,"install_tag":"verified","quickstart_score":80,"quickstart_tag":"verified","pypi_latest":null,"install_checks":{"last_tested":"2026-05-12","tag":"verified","tag_description":"installs cleanly on critical runtimes, fast import, recently tested","results":[{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":1.64,"mem_mb":19.2,"disk_size":"93.5M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":1.38,"mem_mb":19.2,"disk_size":"164M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":1.65,"mem_mb":19,"disk_size":"84.3M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":1.63,"mem_mb":19,"disk_size":"154M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":1.22,"mem_mb":18.1,"disk_size":"80.2M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":1.25,"mem_mb":18.1,"disk_size":"152M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null}]},"quickstart_checks":{"last_tested":"2026-05-12","tag":"verified","tag_description":"quickstart runs on critical runtimes, recently tested","results":[{"runtime":"python:3.10-alpine","exit_code":0},{"runtime":"python:3.10-slim","exit_code":0},{"runtime":"python:3.11-alpine","exit_code":0},{"runtime":"python:3.11-slim","exit_code":0},{"runtime":"python:3.12-alpine","exit_code":0},{"runtime":"python:3.12-slim","exit_code":0},{"runtime":"python:3.13-alpine","exit_code":0},{"runtime":"python:3.13-slim","exit_code":0},{"runtime":"python:3.9-alpine","exit_code":0},{"runtime":"python:3.9-slim","exit_code":0}]}}