{"record":{"id":"38d678c9355bd367","repo":"getredash/redash","slug":"please-use-a-user-api-key","errorCode":null,"errorMessage":"Please use a user API key.","messagePattern":"Please use a user API key\\.","errorType":"http","errorClass":null,"httpStatus":403,"severity":"error","filePath":"redash/handlers/queries.py","lineNumber":461,"sourceCode":"        self.record_event({\"action\": \"fork\", \"object_id\": query_id, \"object_type\": \"query\"})\n\n        return QuerySerializer(forked_query, with_visualizations=True).serialize()\n\n\nclass QueryRefreshResource(BaseResource):\n    def post(self, query_id):\n        \"\"\"\n        Execute a query, updating the query object with the results.\n\n        :param query_id: ID of query to execute\n\n        Responds with query task details.\n        \"\"\"\n        # TODO: this should actually check for permissions, but because currently you can only\n        # get here either with a user API key or a query one, we can just check whether it's\n        # an api key (meaning this is a query API key, which only grants read access).\n        if self.current_user.is_api_user():\n            abort(403, message=\"Please use a user API key.\")\n\n        query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org)\n        require_access(query, self.current_user, not_view_only)\n\n        parameter_values = collect_parameters_from_request(request.args)\n        parameterized_query = ParameterizedQuery(query.query_text, org=self.current_org)\n        should_apply_auto_limit = query.options.get(\"apply_auto_limit\", False)\n        return run_query(parameterized_query, parameter_values, query.data_source, query.id, should_apply_auto_limit)\n\n\nclass QueryTagsResource(BaseResource):\n    def get(self):\n        \"\"\"\n        Returns all query tags including those for drafts.\n        \"\"\"\n        tags = models.Query.all_tags(self.current_user, include_drafts=True)\n        return {\"tags\": [{\"name\": name, \"count\": count} for name, count in tags]}\n","sourceCodeStart":443,"sourceCodeEnd":479,"githubUrl":"https://github.com/getredash/redash/blob/ca79fe988d81cdac9675b412f3dfcab107bc1fbc/redash/handlers/queries.py#L443-L479","documentation":"Raised by QueryResource.post (the query execution/result-job endpoint) in redash/handlers/queries.py when the request authenticates with a query-scoped API key (is_api_user()). Executing or refreshing queries requires a user API key because only a real user can hold modify permissions.","triggerScenarios":"POST /api/queries/<id>/results (or /refresh) using the API key shown on a query's sharing dialog instead of the user profile API key.","commonSituations":"Copy-pasting the wrong key from the UI (query key vs personal key); CI jobs configured with a share key attempting to trigger executions; scripts written against read-only endpoints later upgraded to trigger execution with the same key.","solutions":["Use your personal user API key (found in the user profile Settings page, or via the users API).","If embedding read-only access was the goal, use the query result endpoint (GET latest result) which permits query API keys.","Store the key under a distinct env var (e.g. REDASH_USER_API_KEY) to avoid mixing key types."],"exampleFix":"# before\nheaders = {'Authorization': 'Key <query-scoped-api-key>'}\nclient.post('/api/queries/123/refresh', headers=headers)\n\n# after\nheaders = {'Authorization': 'Key <user-api-key>'}\nclient.post('/api/queries/123/refresh', headers=headers)","handlingStrategy":"validation","validationCode":"# check key type: query keys fail execution, user keys work\nwho = client.get('/api/session')\nassert not who.get('is_api_user', False) or 'query' not in key_scope","typeGuard":null,"tryCatchPattern":"try:\n    client.post(f'/api/queries/{qid}/refresh', headers=h)\nexcept HTTPError as e:\n    if e.response.status_code == 403 and 'user API key' in e.response.text:\n        h['Authorization'] = f'Key {os.environ[\"REDASH_USER_API_KEY\"]}'\n        client.post(f'/api/queries/{qid}/refresh', headers=h)\n    else:\n        raise","preventionTips":["Keep separate env vars for user keys vs query keys.","Use query-scoped keys only for read endpoints (latest result, widgets)."],"tags":["redash","api-key","authorization","forbidden"],"backgroundTag":"wrong-api-key-scope","analyzedSha":"ca79fe988d81cdac9675b412f3dfcab107bc1fbc","analyzedAt":"2026-08-28T18:32:34.637Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}