{"id":22813369,"url":"https://github.com/southclaws/pawn-requests","last_synced_at":"2026-02-03T21:31:52.781Z","repository":{"id":44415117,"uuid":"128465978","full_name":"Southclaws/pawn-requests","owner":"Southclaws","description":"pawn-requests provides an API for interacting with HTTP(S) JSON APIs.","archived":false,"fork":false,"pushed_at":"2026-01-15T11:23:19.000Z","size":300,"stargazers_count":76,"open_issues_count":11,"forks_count":28,"subscribers_count":7,"default_branch":"master","last_synced_at":"2026-01-15T15:52:11.515Z","etag":null,"topics":["http-client","http-requests","pawn-package","requests","sa-mp","sa-mp-development","sa-mp-plugin"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Southclaws.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2018-04-06T20:13:07.000Z","updated_at":"2026-01-15T12:56:38.000Z","dependencies_parsed_at":"2024-12-12T12:31:00.695Z","dependency_job_id":"d80b3fd5-37ef-4fc1-a661-2bc3eb9e3763","html_url":"https://github.com/Southclaws/pawn-requests","commit_stats":null,"previous_names":[],"tags_count":25,"template":false,"template_full_name":null,"purl":"pkg:github/Southclaws/pawn-requests","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Southclaws%2Fpawn-requests","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Southclaws%2Fpawn-requests/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Southclaws%2Fpawn-requests/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Southclaws%2Fpawn-requests/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Southclaws","download_url":"https://codeload.github.com/Southclaws/pawn-requests/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Southclaws%2Fpawn-requests/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29058267,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-03T20:13:53.544Z","status":"ssl_error","status_checked_at":"2026-02-03T20:13:40.507Z","response_time":96,"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":["http-client","http-requests","pawn-package","requests","sa-mp","sa-mp-development","sa-mp-plugin"],"created_at":"2024-12-12T12:27:07.626Z","updated_at":"2026-02-03T21:31:52.763Z","avatar_url":"https://github.com/Southclaws.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pawn-requests\n\n[![GitHub](https://shields.southcla.ws/badge/sampctl-pawn--requests-2f2f2f.svg?style=for-the-badge)](https://github.com/Southclaws/pawn-requests)\n\nThis package provides an API for interacting with HTTP(S) APIs with support for\ntext and JSON data types.\n\n## Installation\n\nSimply install to your project:\n\n```bash\nsampctl package install Southclaws/pawn-requests\n```\n\nInclude in your code and begin using the library:\n\n```pawn\n#include \u003crequests\u003e\n```\n\n## Usage\n\n### Requests\n\nThe Requests API is based on common implementations of similar libraries in\nlanguages such as Go, Python and JS (Node.js).\n\nThere is an example of a basic gamemode that uses requests to store player data\nas JSON [here](https://github.com/Southclaws/pawn-requests-example).\n\n#### Requests Client\n\nFirst you create a `RequestsClient`, you should store this globally:\n\n```pawn\nnew RequestsClient:client;\n\nmain() {\n    client = RequestsClient(\"http://httpbin.org/\");\n}\n```\n\nWhen you create a RequestsClient, you specify the **endpoint** you want to send\nrequests to with that client. This means you don't specify the endpoint for each\nindividual request.\n\nYou can also set headers for the client, these headers will be sent with every\nrequest. This is useful for setting authentication headers for a private\nendpoint:\n\n```pawn\nnew RequestsClient:client;\n\nmain() {\n    client = RequestsClient(\"http://httpbin.org/\", RequestHeaders(\n        \"Authorization\", \"Bearer xyz\"\n    ));\n}\n```\n\nThe `RequestHeaders` function expects an even number of string arguments. It's\ngood practice to lay out your headers in a key-value style, like:\n\n```pawn\nRequestHeaders(\n    \"Authorization\", \"Bearer xyz\",\n    \"Connection\", \"keep-alive\",\n    \"Cache-Control\", \"no-cache\"\n)\n```\n\nBut don't forget these are just normal arguments to a function so watch out for\ntrailing commas!\n\n#### Making Basic Requests\n\nNow you have a client, you can start making requests. If you want to work with\nplain text or any data other than JSON, you use the `Request` function:\n\n```pawn\nRequest(\n    client,\n    \"robots.txt\",\n    HTTP_METHOD_GET,\n    \"OnGetData\",\n    .headers = RequestHeaders()\n);\n\npublic OnGetData(Request:id, E_HTTP_STATUS:status, data[], dataLen) {\n    printf(\"status: %d, data: '%s'\", _:status, data);\n}\n```\n\nUsing the client constructed earlier, this would hit\n`http://httpbin.org/robots.txt` with a GET request and when the request has\nfinished, `OnGetData` would be called and print:\n\n```text\nstatus: 200, data: 'User-agent: *\nDisallow: /deny\n'\n```\n\nThe behaviour is similar to the existing SA:MP `HTTP()` function except this\nsupports headers, a larger body, more methods, HTTPS and is generally safer in\nterms of error handling.\n\n#### Making JSON Requests\n\nJSON requests allow you to inline construct JSON at the request side as well as\naccess JSON objects in the response.\n\nFor example, the endpoint `http://httpbin.org/anything` returns JSON data so we\ncan access that directly as a `Node:` object in the response callback:\n\n```pawn\nRequestJSON(\n    client,\n    \"anything\",\n    HTTP_METHOD_GET,\n    \"OnGetJson\",\n    .headers = RequestHeaders()\n);\n\npublic OnGetJson(Request:id, E_HTTP_STATUS:status, Node:node) {\n    new output[128];\n    JsonGetString(node, \"method\", output);\n    printf(\"anything response: '%s'\", output);\n}\n```\n\nThe `anything` endpoint at httpbin responds with a bunch of related data in JSON\nformat. The `method` field contains the method used to perform the request and\nin this case, the method is `GET` so `OnGetJson` will output\n`anything response: 'GET'`.\n\nAnd you can also send JSON data with a POST method:\n\n```pawn\nRequestJSON(\n    client,\n    \"post\",\n    HTTP_METHOD_POST,\n    \"OnPostJson\",\n    JsonObject(\n        \"playerName\", JsonString(\"Southclaws\"),\n        \"kills\", JsonInt(5),\n        \"topThreeWeapons\", JsonArray(\n            JsonString(\"M4\"),\n            JsonString(\"MP5\"),\n            JsonString(\"Desert Eagle\")\n        )\n    ),\n    .headers = RequestHeaders()\n);\n\npublic OnPostJson(Request:id, E_HTTP_STATUS:status, Node:node) {\n    if(status == HTTP_STATUS_CREATED) {\n        printf(\"successfully posted JSON!\");\n    }\n}\n```\n\nYou could quite easily build a JSON-driven storage server backed by MongoDB.\n\nSee the JSON section below for examples of manipulating JSON `Node:` objects.\n\nSee the\n[pawn-requests-example](https://github.com/Southclaws/pawn-requests-example)\nrepository for a more full example of using requests and JSON together.\n\n#### Request Failures\n\nIf a request fails for any reason, `OnRequestFailure` is called with the\nfollowing signature: `(Request:id, errorCode, errorMessage[], len)` where\n`errorCode` and `errorMessage` contain information to help you debug the\nrequest.\n\n#### Keeping Track of Request IDs\n\nBoth `Request` and `RequestJSON` return a `Request:` tagged value. This value is\nthe request identifier and is unique during server runtime, same as how\n`SetTimer` returns a unique ID.\n\nBecause responses are asynchronous and the data comes back in a callback at a\nlater time, most of the time you will have to store this ID so you know which\nrequest triggered which response.\n\nYou cannot simply use the ID as an index to an array because it's an\nautomatically incrementing value and thus is unbounded. You should instead use\nBigETI's pawn-map plugin to map request IDs to some other data - such as the\nplayer/vehicle/house/etc that triggered the request. See the\n[pawn-requests-example](https://github.com/Southclaws/pawn-requests-example) for\nan example of this.\n\n### JSON\n\nIf you don't already know what JSON is, a good place to start is\n[MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON).\nIt's pretty much a web API standard nowadays (Twitter, Discord, GitHub and just\nabout every other API uses it to represent data). I'll briefly go over it before\ngetting into the API.\n\nThis plugin stores JSON values as \"Nodes\". Each node represents a value of one\ntype. Here are some examples of the representations of different node types:\n\n*   `{}` - Object that is empty\n*   `{\"key\": \"value\"}` - Object with one key that points to a String node\n*   `\"hello\"` - String\n*   `1` - Number (integer)\n*   `1.5` - Number (floating point)\n*   `[1, 2, 3]` - Array, of Number nodes\n*   `[{}, {}]` - Array of empty Object nodes\n*   `true` - Boolean\n\nThe main point here is that everything is a node, even Objects and Arrays that\ncontain other nodes.\n\n#### Building an Object\n\nTo build a JSON object to be sent in a request, you most likely want to start\nwith `JsonObject` however you can use any node as the root node, it depends on\nwhere you're sending the data but for this example I'll use an Object as the\nroot node.\n\n```pawn\nnew Node:node = JsonObject();\n```\n\nThis just constructs an empty object and if you \"stringify\" it (stringify simply\nmeans to turn into a string) you get:\n\n```json\n{}\n```\n\nSo to add more nodes to this object, simply add parameters, as key-value pairs:\n\n```pawn\nnew Node:node = JsonObject(\n    \"key\", JsonString(\"value\")\n);\n```\n\nThis would stringify as:\n\n```json\n{\n    \"key\": \"value\"\n}\n```\n\nYou can nest objects within objects too:\n\n```pawn\nnew Node:node = JsonObject(\n    \"key\", JsonObject(\n        \"key\", JsonString(\"value\")\n    )\n);\n```\n\n```json\n{\n    \"key\": {\n        \"key\": \"value\"\n    }\n}\n```\n\nAnd do arrays of any node:\n\n```pawn\nnew Node:node = JsonObject(\n    \"key\", JsonArray(\n        JsonString(\"one\"),\n        JsonString(\"two\"),\n        JsonString(\"three\"),\n        JsonObject(\n            \"more_stuff1\", JsonString(\"uno\"),\n            \"more_stuff2\", JsonString(\"dos\"),\n            \"more_stuff3\", JsonString(\"tres\")\n        )\n    )\n);\n```\n\nSee the\n[unit tests](https://github.com/Southclaws/pawn-requests/blob/master/test.pwn)\nfor more examples of JSON builders.\n\n#### Accessing Data\n\nWhen you request JSON data, it's provided as a `Node:` in the callback. Most of\nthe time, you'll get an object back but depending on the application that\nresponded this could differ.\n\nLets assume this request responds with the following data:\n\n```json\n{\n    \"name\": \"Southclaws\",\n    \"score\": 45,\n    \"vip\": true,\n    \"inventory\": [\n        {\n            \"name\": \"M4\",\n            \"ammo\": 341\n        },\n        {\n            \"name\": \"Desert Eagle\",\n            \"ammo\": 32\n        }\n    ]\n}\n```\n\n```pawn\npublic OnSomeResponse(Request:id, E_HTTP_STATUS:status, Node:json) {\n    new ret;\n\n    new name[MAX_PLAYER_NAME];\n    ret = JsonGetString(node, \"name\", name);\n    if(ret) {\n        err(\"failed to get name, error: %d\", ret);\n        return 1;\n    }\n\n    new score;\n    ret = JsonGetInt(node, \"score\", score);\n    if(ret) {\n        err(\"failed to get score, error: %d\", ret);\n        return 1;\n    }\n\n    new bool:vip;\n    ret = JsonGetBool(node, \"vip\", vip);\n    if(ret) {\n        err(\"failed to get vip, error: %d\", ret);\n        return 1;\n    }\n\n    new Node:inventory;\n    ret = JsonGetArray(node, \"inventory\", inventory);\n    if(ret) {\n        err(\"failed to get inventory, error: %d\", ret);\n        return 1;\n    }\n\n    new length;\n    ret = JsonArrayLength(inventory, length);\n    if(ret) {\n        err(\"failed to get inventory array length, error: %d\", ret);\n        return 1;\n    }\n\n    for(new i; i \u003c length; ++i) {\n        new Node:item;\n        ret = JsonArrayObject(inventory, i, item);\n        if(ret) {\n            err(\"failed to get inventory item %d, error: %d\", i, ret);\n            return 1;\n        }\n\n        new itemName[32];\n        ret = JsonGetString(item, \"name\", itemName);\n        if(ret) {\n            err(\"failed to get inventory item %d, error: %d\", i, ret);\n            return 1;\n        }\n\n        new itemAmmo;\n        ret = JsonGetInt(item, \"name\", itemAmmo);\n        if(ret) {\n            err(\"failed to get inventory item %d, error: %d\", i, ret);\n            return 1;\n        }\n\n        printf(\"item %d name: %s ammo: %d\", itemName, itemAmmo);\n    }\n\n    return 0;\n}\n```\n\nIn this example, we extract each field from the JSON object with full error\nchecking. This example shows usage of object and array access as well as\nprimitives such as strings, integers and a boolean.\n\nIf you're not a fan of the overly terse and explicit error checking, you can\nalternatively just check your errors at the end but this will mean you won't\nknow exactly _where_ an error occurred, just that it did.\n\n```pawn\nnew ret;\nret += JsonGetString(node, \"key1\", value1);\nret += JsonGetString(node, \"key2\", value2);\nret += JsonGetString(node, \"key3\", value3);\nif(ret) {\n    err(\"some error occurred: %d\", ret);\n}\n```\n\n## Testing\n\nTo run unit tests for the plugin on Windows, first build the plugin with Visual\nStudio by opening the `CMakeLists.txt` via the `File \u003e Open \u003e CMake` menu and\nthen building the project. You will need to pull the dependencies too so make\nsure you've done `git submodule init \u0026\u0026 git submodule update` or cloned the\nrepository recursively.\n\nOnce you've done that, the .dll files will be in `./test/plugins/Debug`. There\nis also a `-release` suffixed version of this make command for testing the\nrelease binaries.\n\n```powershell\nmake test-windows-debug\n```\n\nIf you want to build and test the Linux version from a Windows machine, make\nsure Docker is installed and run:\n\n```powershell\nmake build-linux\n```\n\nWhich will output `requests.so` to `./test/plugins`. To run unit tests on Linux,\nrun:\n\n```powershell\nmake test-linux\n```\n\nWhich will run the tests via sampctl with the `--container` flag set.\n\n## Development\n\nTo set up the development environment, first install\n[`vcpkg`](https://github.com/Microsoft/vcpkg) then\n[cpprestsdk](https://github.com/Microsoft/cpprestsdk).\n\nOpen Visual Studio (A recent version with CMake support) and File \u003e Open the\nproject `CMakeLists.txt`. VS will fail on the first attempt as it won't be able\nto find cpprestsdk. To resolve this, edit `.vs/CMakeSettings.json` to contain\nthe necessary environment variables for a Debug and Release configuration:\n\n```json\n{\n  \"configurations\": [\n    {\n      \"name\": \"x86-Release\",\n      \"generator\": \"Visual Studio 15 2017\",\n      \"configurationType\": \"Release\",\n      \"buildRoot\":\n        \"${env.USERPROFILE}\\\\CMakeBuilds\\\\${workspaceHash}\\\\build\\\\${name}\",\n      \"cmakeCommandArgs\": \"\",\n      \"buildCommandArgs\": \"-m -v:minimal\",\n      \"variables\": [\n        {\n          \"name\": \"CMAKE_TOOLCHAIN_FILE\",\n          \"value\": \"C:/Users/Southclaws/vcpkg/scripts/buildsystems/vcpkg.cmake\"\n        }\n      ]\n    },\n    {\n      \"name\": \"x86-Debug\",\n      \"generator\": \"Visual Studio 15 2017\",\n      \"configurationType\": \"Debug\",\n      \"buildRoot\":\n        \"${env.USERPROFILE}\\\\CMakeBuilds\\\\${workspaceHash}\\\\build\\\\${name}\",\n      \"cmakeCommandArgs\": \"\",\n      \"buildCommandArgs\": \"-m -v:minimal\",\n      \"variables\": [\n        {\n          \"name\": \"CMAKE_TOOLCHAIN_FILE\",\n          \"value\": \"C:/Users/Southclaws/vcpkg/scripts/buildsystems/vcpkg.cmake\"\n        }\n      ]\n    }\n  ]\n}\n```\n\nThe configuration file may change depending on VS version or other things, as\nlong as the `CMAKE_TOOLCHAIN_FILE` variables are passed to CMake properly, the\nbuild should succeed.\n\nOnce this is done, VS should start indexing all the dependencies. Once it has\nfinihed, in the menu bar, hit CMake \u003e Build All and it should spit out a `.dll`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsouthclaws%2Fpawn-requests","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsouthclaws%2Fpawn-requests","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsouthclaws%2Fpawn-requests/lists"}