{"id":13508305,"url":"https://github.com/benoitc/hackney","last_synced_at":"2025-05-14T23:01:45.861Z","repository":{"id":41125096,"uuid":"4864481","full_name":"benoitc/hackney","owner":"benoitc","description":"simple HTTP client in Erlang","archived":false,"fork":false,"pushed_at":"2025-02-26T17:26:42.000Z","size":7392,"stargazers_count":1349,"open_issues_count":108,"forks_count":434,"subscribers_count":56,"default_branch":"master","last_synced_at":"2025-05-07T22:01:55.579Z","etag":null,"topics":["client","erlang","http"],"latest_commit_sha":null,"homepage":"","language":"Erlang","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/benoitc.png","metadata":{"files":{"readme":"README.md","changelog":"NEWS.md","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}},"created_at":"2012-07-02T20:24:40.000Z","updated_at":"2025-05-01T09:33:30.000Z","dependencies_parsed_at":"2024-01-22T22:17:23.829Z","dependency_job_id":"3bacc0d4-eb25-4027-9fdc-486b0685ea69","html_url":"https://github.com/benoitc/hackney","commit_stats":{"total_commits":1122,"total_committers":167,"mean_commits":6.718562874251497,"dds":"0.25668449197860965","last_synced_commit":"eca5fbb1ff2d84facefb2a633e00f6ca16e7ddfd"},"previous_names":[],"tags_count":112,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benoitc%2Fhackney","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benoitc%2Fhackney/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benoitc%2Fhackney/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benoitc%2Fhackney/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/benoitc","download_url":"https://codeload.github.com/benoitc/hackney/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254037076,"owners_count":22003735,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["client","erlang","http"],"created_at":"2024-08-01T02:00:51.139Z","updated_at":"2025-05-14T23:01:45.820Z","avatar_url":"https://github.com/benoitc.png","language":"Erlang","readme":"\n\n# hackney - HTTP client library in Erlang #\n\nCopyright (c) 2012-2025 Benoît Chesneau.\n\n__Version:__ 1.23.0\n\n# hackney\n\n**hackney** is an HTTP client library for Erlang.\n\n[![Build Status](https://github.com/benoitc/hackney/workflows/build/badge.svg)](https://github.com/benoitc/hackney/actions?query=workflow%3Abuild)\n[![Hex pm](http://img.shields.io/hexpm/v/hackney.svg?style=flat)](https://hex.pm/packages/hackney)\n\n## Main features:\n\n- no message passing (except for asynchronous responses): response is\n  directly streamed to the current process and state is kept in a `#client{}` record.\n- binary streams\n- SSL support\n- Keepalive handling\n- basic authentication\n- stream the response and the requests\n- fetch a response asynchronously\n- multipart support (streamed or not)\n- chunked encoding support\n- Can send files using the sendfile API\n- Optional socket pool\n- REST syntax: `hackney:Method(URL)` (where a method can be get, post, put, delete, ...)\n\n**Supported versions** of Erlang are 22.3 and above. It is\nreported to work with version from 19.3 to 21.3.\n\n\u003e Note: This is a work in progress, see the\n[TODO](http://github.com/benoitc/hackney/blob/master/TODO.md) for more\ninformation on what still needs to be done.\n\n#### Useful modules are:\n\n- [`hackney`](https://hexdocs.pm/hackney/hackney.html): main module. It contains all HTTP client functions.\n- [`hackney_http`](https://hexdocs.pm/hackney/hackney_http.html): HTTP parser in pure Erlang. This parser is able\nto parse HTTP responses and requests in a streaming fashion. If not set\nit will be autodetected if it's a request or a response that's needed.\n\n- [`hackney_headers`](https://hexdocs.pm/hackney/hackney_headers.html) Module to manipulate HTTP headers.\n- [`hackney_cookie`](https://hexdocs.pm/hackney/hackney_cookie.html): Module to manipulate cookies.\n- [`hackney_multipart`](https://hexdocs.pm/hackney/hackney_multipart.html): Module to encode/decode multipart.\n- [`hackney_url`](https://hexdocs.pm/hackney/hackney_url.html): Module to parse and create URIs.\n- [`hackney_date`](https://hexdocs.pm/hackney/hackney_date.html): Module to parse HTTP dates.\n\nRead the [NEWS](https://hexdocs.pm/hackney/news.html) file\nto get the last changelog.\n\n## Installation\n\nDownload the sources from our [Github\nrepository](http://github.com/benoitc/hackney)\n\nTo build the application simply run 'rebar3 compile'.\n\nTo run tests run 'rebar3 eunit'.\nTo generate doc, run 'rebar3 edoc'.\n\nOr add it to your rebar config\n\n```erlang\n\n{deps, [\n    ....\n    {hackney, \".*\", {git, \"git://github.com/benoitc/hackney.git\", {branch, \"master\"}}}\n]}.\n```\n\n## Basic usage\n\nThe basic usage of hackney is:\n\n### Start hackney\n\nhackney is an\n[OTP](http://www.erlang.org/doc/design_principles/users_guide.html)\napplication. You have to start it first before using any of the functions.\nThe hackney application will start the default socket pool for you.\n\nTo start in the console run:\n\n```erlang-repl\n\n$ ./rebar3 shell\n\n```\n\nIt is suggested that you install rebar3 user-wide as described [here](http://blog.erlware.org/rebar3-features-part-1-local-install-and-upgrade/).\nThis fixes zsh (and maybe other shells) escript-related bugs. Also this should speed things up.\n\n```erlang\n\n\u003e application:ensure_all_started(hackney).\nok\n```\n\nIt will start hackney and all of the application it depends on:\n\n```erlang\n\napplication:start(crypto),\napplication:start(public_key),\napplication:start(ssl),\napplication:start(hackney).\n```\n\nOr add hackney to the applications property of your .app in a release\n\n### Simple request\n\nDo a simple request that will return a client state:\n\n```erlang\n\nMethod = get,\nURL = \u003c\u003c\"https://friendpaste.com\"\u003e\u003e,\nHeaders = [],\nPayload = \u003c\u003c\u003e\u003e,\nOptions = [],\n{ok, StatusCode, RespHeaders, ClientRef} = hackney:request(Method, URL,\n                                                        Headers, Payload,\n                                                        Options).\n```\n\nThe request method returns the tuple `{ok, StatusCode, Headers, ClientRef}`\nor `{error, Reason}`. A `ClientRef` is simply a reference to the current\nrequest that you can reuse.\n\nIf you prefer the REST syntax, you can also do:\n\n```erlang\nhackney:Method(URL, Headers, Payload, Options)\n```\n\nwhere `Method`, can be any HTTP method in lowercase.\n\n### Read the body\n\n```erlang\n{ok, Body} = hackney:body(ClientRef).\n```\n\n`hackney:body/1` fetch the body. To fetch it by chunk you can use the\n`hackney:stream_body/1` function:\n\n```erlang\n\nread_body(MaxLength, Ref, Acc) when MaxLength \u003e byte_size(Acc) -\u003e\n\tcase hackney:stream_body(Ref) of\n\t\t{ok, Data} -\u003e\n\t\t\tread_body(MaxLength, Ref, \u003c\u003c Acc/binary, Data/binary \u003e\u003e);\n\t\tdone -\u003e\n\t\t\t{ok, Acc};\n\t\t{error, Reason} -\u003e\n\t\t\t{error, Reason}\n\tend.\n```\n\n\u003e Note: you can also fetch a multipart response using the functions\n\u003e `hackney:stream_multipart/1` and  `hackney:skip_multipart/1`.\n\n\u003e Note 2: using the `with_body` option will return the body directly instead of a reference.\n\n### Reuse a connection\n\nBy default all connections are created and closed dynamically by\nhackney but sometimes you may want to reuse the same reference for your\nconnections. It's especially useful if you just want to handle serially a\ncouple of requests.\n\n\u003e A closed connection will automatically be reconnected.\n\n#### To create a connection:\n\n```erlang\n\nTransport = hackney_ssl,\nHost = \u003c\u003c \"friendpaste.com\" \u003e\u003e,\nPort = 443,\nOptions = [],\n{ok, ConnRef} = hackney:connect(Transport, Host, Port, Options).\n```\n\n\u003e To create a connection that will use an HTTP proxy use\n\u003e `hackney_http_proxy:connect_proxy/5` instead.\n\n#### To get local and remote ip and port information of a connection:\n\n```erlang\n\n\u003e hackney:peername(ConnRef).\n\u003e hackney:sockname(ConnRef).\n```\n\n#### Make a request\n\nOnce you created a connection use the `hackney:send_request/2` function\nto make a request:\n\n```erlang\n\nReqBody = \u003c\u003c \"{\t\\\"snippet\\\": \\\"some snippet\\\" }\" \u003e\u003e,\nReqHeaders = [{\u003c\u003c\"Content-Type\"\u003e\u003e, \u003c\u003c\"application/json\"\u003e\u003e}],\nNextPath = \u003c\u003c\"/\"\u003e\u003e,\nNextMethod = post,\nNextReq = {NextMethod, NextPath, ReqHeaders, ReqBody},\n{ok, _, _, ConnRef} = hackney:send_request(ConnRef, NextReq),\n{ok, Body1} = hackney:body(ConnRef).\n```\n\nHere we are posting a JSON payload to '/' on the friendpaste service to\ncreate a paste. Then we close the client connection.\n\n\u003e If your connection supports keepalive the connection will be kept open until you close it exclusively.\n\n### Send a body\n\nhackney helps you send different payloads by passing different terms as\nthe request body:\n\n- `{form, PropList}` : To send a form\n- `{multipart, Parts}` : to send your body using the multipart API. Parts\n  follow this format:\n  - `eof`: end the multipart request\n  - `{file, Path}`: to stream a file\n  - `{file, Path, ExtraHeaders}`: to stream a file\n  - `{file, Path, Name, ExtraHeaders}` : to send a file with DOM element name and extra headers\n  - `{Name, Content}`: to send a full part\n  - `{Name, Content, ExtraHeaders}`: to send a full part\n  - `{mp_mixed, Name, MixedBoundary}`: To notify we start a part with \n    a mixed multipart content\n  - `{mp_mixed_eof, MixedBoundary}`: To notify we end a part with a\n    mixed multipart content\n- `{file, File}` : To send a file\n- Bin: To send a binary or an iolist\n\n\u003e Note: to send a chunked request, just add the `Transfer-Encoding: chunked`\n\u003e header to your headers. Binary and Iolist bodies will be then sent using\n\u003e the chunked encoding.\n\n#### Send the body by yourself\n\nWhile the default is to directly send the request and fetch the status\nand headers, if the body is set as the atom `stream` the request and\nsend_request function will return {ok, Client}. Then you can use the\nfunction `hackney:send_body/2` to stream the request body and\n`hackney:start_response/1` to initialize the response.\n\n\u003e Note: The function `hackney:start_response/1` will only accept\n\u003e a Client that is waiting for a response (with a response state\n\u003e equal to the atom `waiting`).\n\nEx:\n\n```erlang\n\nReqBody = \u003c\u003c \"{\n      \\\"id\\\": \\\"some_paste_id2\\\",\n      \\\"rev\\\": \\\"some_revision_id\\\",\n      \\\"changeset\\\": \\\"changeset in unidiff format\\\"\n}\" \u003e\u003e,\nReqHeaders = [{\u003c\u003c\"Content-Type\"\u003e\u003e, \u003c\u003c\"application/json\"\u003e\u003e}],\nPath = \u003c\u003c\"https://friendpaste.com/\"\u003e\u003e,\nMethod = post,\n{ok, ClientRef} = hackney:request(Method, Path, ReqHeaders, stream, []),\nok  = hackney:send_body(ClientRef, ReqBody),\n{ok, _Status, _Headers, ClientRef} = hackney:start_response(ClientRef),\n{ok, Body} = hackney:body(ClientRef),\n```\n\n\u003e Note: to send a **multipart** body  in a streaming fashion use the\n\u003e `hackney:send_multipart_body/2` function.\n\n### Get a response asynchronously\n\nSince the 0.6 version, hackney is able to fetch the response\nasynchronously using the `async` option:\n\n```erlang\n\nUrl = \u003c\u003c\"https://friendpaste.com/_all_languages\"\u003e\u003e,\nOpts = [async],\nLoopFun = fun(Loop, Ref) -\u003e\n        receive\n            {hackney_response, Ref, {status, StatusInt, Reason}} -\u003e\n                io:format(\"got status: ~p with reason ~p~n\", [StatusInt,\n                                                              Reason]),\n                Loop(Loop, Ref);\n            {hackney_response, Ref, {headers, Headers}} -\u003e\n                io:format(\"got headers: ~p~n\", [Headers]),\n                Loop(Loop, Ref);\n            {hackney_response, Ref, done} -\u003e\n                ok;\n            {hackney_response, Ref, Bin} -\u003e\n                io:format(\"got chunk: ~p~n\", [Bin]),\n                Loop(Loop, Ref);\n\n            Else -\u003e\n                io:format(\"else ~p~n\", [Else]),\n                ok\n        end\n    end.\n\n{ok, ClientRef} = hackney:get(Url, [], \u003c\u003c\u003e\u003e, Opts),\nLoopFun(LoopFun, ClientRef).\n```\n\n\u003e **Note 1**: When `{async, once}` is used the socket will receive only once.\n\u003e To receive the other messages use the function `hackney:stream_next/1`.\n\n\u003e **Note 2**:  Asynchronous responses automatically checkout the socket at the end.\n\n\u003e **Note 3**:  At any time you can go back and receive your response\n\u003e synchronously using the function `hackney:stop_async/1` See the\n\u003e example [test_async_once2](https://github.com/benoitc/hackney/blob/master/examples/test_async_once2.erl) for the usage.\n\n\u003e **Note 4**:  When the option `{follow_redirect, true}` is passed to\n\u003e the request, you will receive the following messages on valid\n\u003e redirection:\n\u003e - `{redirect, To, Headers}`\n\u003e - `{see_other, To, Headers}` for status 303 and POST requests.\n\n\u003e **Note 5**: You can send the messages to another process by using the\n\u003e option `{stream_to, Pid}` .\n\n### Use the default pool\n\nHackney uses socket pools to reuse connections globally. By default,\nhackney uses a pool named `default`. You may want to use different\npools in your application which allows you to maintain a group of\nconnections. To use a different pool, do the following:\n\n```erlang\n\nMethod = get,\nURL = \u003c\u003c\"https://friendpaste.com\"\u003e\u003e,\nHeaders = [],\nPayload = \u003c\u003c\u003e\u003e,\nOptions = [{pool, mypool}],\n{ok, StatusCode, RespHeaders, ClientRef} = hackney:request(Method, URL, Headers,\n                                                        Payload, Options).\n```\n\nBy adding the tuple `{pool, mypool}` to the options, hackney will use\nthe connections stored in that pool. The pool gets started automatically\nthe first time it is used. You can also explicitly configure and start\nthe pool like this:\n\n```erlang\n\nPoolName = mypool,\nOptions = [{timeout, 150000}, {max_connections, 100}],\nok = hackney_pool:start_pool(PoolName, Options),\n```\n\n`timeout` is the time we keep the connection alive in the pool,\n`max_connections` is the number of connections maintained in the pool. Each\nconnection in a pool is monitored and closed connections are removed\nautomatically.\n\nTo close a pool do:\n\n```erlang\nhackney_pool:stop_pool(PoolName).\n```\n\n\u003e Note: Sometimes you want to disable the default pool in your app\n\u003e without having to set the client option each time. You can now do this\n\u003e by setting the hackney application environment key `use_default_pool`\n\u003e to false. This means that hackney will not use socket pools unless\n\u003e specifically requested using the `pool` option as described above.\n\u003e\n\u003e To disable socket pools for a single request, specify the option\n\u003e `{pool, false}`.\n\n### Use a custom pool handler.\n\nSince the version 0.8 it is now possible to use your own Pool to\nmaintain the connections in hackney.\n\nA pool handler is a module that handles the `hackney_pool_handler`\nbehaviour.\n\nSee for example the\n[hackney_disp](https://github.com/benoitc/hackney_disp) a load-balanced\nPool dispatcher based on dispcount.\n\n\u003e Note: for now you can`t force the pool handler / client.\n\n### Automatically follow a redirection\n\nIf the option `{follow_redirect, true}` is given to the request, the\nclient will be able to automatically follow the redirection and\nretrieve the body. The maximum number of connections can be set using the\n`{max_redirect, Max}` option. Default is 5.\n\nThe client will follow redirects on 301, 302 \u0026 307 if the method is\nget or head. If another method is used the tuple\n`{ok, maybe_redirect, Status, Headers, Client}` will be returned. It will\nonly follow 303 redirects (see other) if the method is a POST.\n\nLast Location is stored in the `location` property of the client state.\n\nex:\n\n```erlang\n\nMethod = get,\nURL = \"http://friendpaste.com/\",\nReqHeaders = [{\u003c\u003c\"accept-encoding\"\u003e\u003e, \u003c\u003c\"identity\"\u003e\u003e}],\nReqBody = \u003c\u003c\u003e\u003e,\nOptions = [{follow_redirect, true}, {max_redirect, 5}],\n{ok, S, H, Ref} = hackney:request(Method, URL, ReqHeaders,\n                                     ReqBody, Options),\n{ok, Body1} = hackney:body(Ref).\n```\n\n### Use SSL/TLS with self signed certificates\n\nHackney uses CA bundles adapted from Mozilla by\n[certifi](https://hex.pm/packages/certifi).\nRecognising an organisation specific (self signed) certificates is possible\nby providing the necessary `ssl_options`. Note that `ssl_options` overrides all\noptions passed to the ssl module.\n\nex (\u003e= Erlang 21):\n\n```erlang\n\nCACertFile = \u003cpath_to_self_signed_ca_bundle\u003e,\nCrlCheckTimeout = 5000,\nSSLOptions = [\n{verify, verify_peer},\n{versions, ['tlsv1.2']},\n{cacertfile, CACertFile},\n{crl_check, peer},\n{crl_cache, {ssl_crl_cache, {internal, [{http, CrlCheckTimeout}]}}},\n{customize_hostname_check,\n  [{match_fun, public_key:pkix_verify_hostname_match_fun(https)}]}],\n\nMethod = get,\nURL = \"http://my-organisation/\",\nReqHeaders = [],\nReqBody = \u003c\u003c\u003e\u003e,\nOptions = [{ssl_options, SSLoptions}],\n{ok, S, H, Ref} = hackney:request(Method, URL, ReqHeaders,\n                                  ReqBody, Options),\n\n%% To provide client certificate:\n\nCertFile = \u003cpath_to_client_certificate\u003e,\nKeyFile = \u003cpath_to_client_private_key\u003e,\nSSLOptions1 = SSLoptions ++ [\n{certfile, CertFile},\n{keyfile, KeyFile}\n],\nOptions1 = [{ssl_options, SSLoptions1}],\n{ok, S1, H1, Ref1} = hackney:request(Method, URL, ReqHeaders,\n                                     ReqBody, Options1).\n\n```\n\n### Proxy a connection\n\n#### HTTP Proxy\n\nTo use an HTTP tunnel add the option `{proxy, ProxyUrl}` where\n`ProxyUrl` can be a simple url or an `{Host, Port}` tuple. If you need\nto authenticate set the option `{proxy_auth, {User, Password}}`.\n\n#### SOCKS5 proxy\n\nHackney supports the connection via a socks5 proxy. To set a socks5\nproxy, use the following settings:\n\n- `{proxy, {socks5, ProxyHost, ProxyPort}}`: to set the host and port of\n  the proxy to connect.\n- `{socks5_user, Username}`: to set the user used to connect to the proxy\n- `{socks5_pass, Password}`: to set the password used to connect to the proxy\n\nSSL and TCP connections can be forwarded via a socks5 proxy. hackney is\nautomatically upgrading to an SSL connection if needed.\n\n### Metrics\n\nHackney offers the following metrics\n\nYou can enable metrics collection by adding a `mod_metrics` entry to hackney's\napp config. Metrics are disabled by default. The module specified must have an\nAPI matching that of the hackney metrics module.\n\nTo  use [folsom](https://github.com/boundary/folsom), specify `{mod_metrics,\nfolsom}`, or if you want to use\n[exometer](https://github.com/feuerlabs/exometer), specify`{mod_metrics,\nexometer}` and ensure that folsom or exometer is in your code path and has\nbeen started.\n\n#### Generic Hackney metrics\n\n|Name                     |Type   | Description                        |\n|-------------------------|-------|------------------------------------|\n|hackney.nb_requests      |counter| Number of running requests         |\n|hackney.total_requests   |counter| Total number of requests           |\n|hackney.finished_requests|counter| Total number of requests finished  |\n\n#### Metrics per Hosts\n\n|Name                              |Type     | Description                               |\n|----------------------------------|---------|-------------------------------------------|\n|hackney.HOST.nb_requests          |counter  | Number of running requests                |\n|hackney.HOST.request_time         |histogram| Request time                              |\n|hackney.HOST.connect_time         |histogram| Connect time                              |\n|hackney.HOST.response_time        |histogram| Response time                             |\n|hackney.HOST.connect_timeout      |counter  | Number of connect timeout                 |\n|hackney.HOST.connect_error        |counter  | Number of timeout errors                  |\n|hackney_pool.HOST.new_connection  |counter  | Number of new pool connections per host   |\n|hackney_pool.HOST.reuse_connection|counter  | Number of reused pool connections per host|\n\n#### Metrics per Pool\n\n|Name                              |Type     | Description                                                          |\n|----------------------------------|---------|----------------------------------------------------------------------|\n|hackney_pool.POOLNAME.take_rate   |meter    | meter recording rate at which a connection is retrieved from the pool|\n|hackney_pool.POOLNAME.no_socket   |counter  | Count of new connections                                             |\n|hackney_pool.POOLNAME.in_use_count|histogram| How many connections from the pool are used                          |\n|hackney_pool.POOLNAME.free_count  |histogram| Number of free sockets in the pool                                   |\n|hackney_pool.POOLNAME.queue_count |histogram| queued clients                                                       |\n\n## Contribute\n\nFor issues, comments or feedback please [create an\nissue](http://github.com/benoitc/hackney/issues).\n\n### Notes for developers\n\nIf you want to contribute patches or improve the docs, you will need to\nbuild hackney using the `rebar_dev.config`  file. It can also be built\nusing the **Makefile**:\n\n```sh\n\n$ rebar3 update\n$ rebar3 compile\n\n```\n\nFor successfully running the hackney test suite locally it is necessary to\ninstall [httpbin](https://pypi.python.org/pypi/httpbin/0.2.0).\n\nAn example installation using virtualenv::\n\n```sh\n\n$ mkvirtualenv hackney\n$ pip install gunicorn httpbin\n\n```\n\nRunning the tests:\n\n```\n$ gunicorn --daemon --pid httpbin.pid httpbin:app\n$ rebar3 eunit\n$ kill `cat httpbin.pid`\n```\n","funding_links":[],"categories":["HTTP","Erlang","Erlang Tools, Libraries, and Frameworks","Tools"],"sub_categories":["E-Books","Mesh networks"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenoitc%2Fhackney","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbenoitc%2Fhackney","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenoitc%2Fhackney/lists"}