awscurl, signed AWS API requests behind a curl-style interface
curl-like access to AWS resources with AWS Signature Version 4 request signing.
At a glance
- What is it?
- awscurl is a small Python tool that signs AWS API requests with Signature Version 4 and sends them through a command line that looks like curl. It is useful when you want the raw HTTP request and the raw response, with credentials taken from the environment or from ~/.aws/credentials.
- Who is it for?
- awscurl suits an engineer who wants to see the exact HTTP request and response an AWS API call produces, who is scripting against an endpoint the AWS CLI does not expose, or who needs to demonstrate a signed request to someone else. It does not suit everyday AWS administration, where the AWS CLI has service-aware commands, pagination and typed output.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 13 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The one thing curl cannot do on its own
Requests to the AWS API must be signed, and awscurl automates that signing so that a request to an AWS service looks like a standard curl command. The project description is exactly that: a curl-like tool with AWS Signature Version 4 request signing.
That is the entire scope, and it is narrower than it might sound. It is not an AWS SDK and not a service client. There is no S3 command, no EC2 command and no pagination logic, because there is no model of any AWS service underneath. What there is, is a signed HTTP request.
The reason this is still useful is that the AWS query APIs are, for many services, just HTTP endpoints with a service name, a region and a body. The README's EC2 example is a DescribeRegions call expressed as a URL with an Action and a Version query parameter, and the response comes back as the raw XML the service produced. For debugging, for an endpoint the official tooling does not wrap, or for a teaching example of what a signed request looks like, that is the point.
The tool supports IAM profile credentials, and the dependencies show how it signs. setup.py pulls in boto3, botocore and awscrt alongside requests, configargparse, configparser and urllib3, so the signing itself comes from the AWS SDK's own signing machinery rather than from a hand-rolled implementation, and the request is sent with requests.
Where the credentials come from, and which ones win
The credential handling follows a simple precedence rule, stated in the options help as: in general, command-line values override environment variables which override defaults.
Each credential has a flag and an environment variable behind it. --access_key reads AWS_ACCESS_KEY_ID, --secret_key reads AWS_SECRET_ACCESS_KEY, --security_token reads AWS_SECURITY_TOKEN and --session_token reads AWS_SESSION_TOKEN, with --region bound to AWS_DEFAULT_REGION and defaulting to us-east-1, and --profile bound to AWS_PROFILE and defaulting to default.
If neither --access_key nor --secret_key is given, and the environment variables are not set either, awscurl attempts to use the credentials in ~/.aws/credentials. If neither --profile nor AWS_PROFILE is set, it uses the default profile.
That fallback is the detail worth understanding before you run the tool in a shared environment. A shell that exports AWS_PROFILE but not keys will sign with a named profile, and a shell with nothing set will silently use whatever the default profile in ~/.aws/credentials holds. The tool does not tell you which credential source it used unless you ask for verbose output.
The AWS_SECURITY_TOKEN and AWS_SESSION_TOKEN pair is worth a note as well. They exist for temporary credentials, where a session token accompanies a key pair that expires, and the fact that both names are accepted suggests the tool has to cover older and newer conventions for the same thing.
A curl option surface with a few AWS extras
Most of the options are the ones you already know from curl, and they behave the way you would expect. -X sets the request method with a default of GET, -d supplies HTTP POST data, -H sets a header, -k allows insecure server connections over SSL, -L follows redirects, and -o writes to a file instead of stdout. The tool adds -v for verbose output and -i to include headers in the output.
Two of the curl options are worth calling out because they behave unusually. --fail-with-body fails on HTTP errors but still saves the body, which is the behaviour you want when the error payload is the useful part, and --data-binary processes POST data exactly as specified with no extra processing whatsoever, which matters when the body is already encoded or signed by something else.
The AWS-specific options are --service, --region and --profile, plus the four credential options. The --service default is execute-api, which is worth noting because it is not inferred from the URL. If you call an S3 endpoint without passing --service s3, the request is signed for the wrong service and AWS will reject it, and the error will look like a signature mismatch rather than a missing flag.
The project also keeps a config file path open, since configargparse and configparser are dependencies, so options can come from a configuration file as well as the command line and the environment.
Three calls that show what the tool is for
The examples in the README are the quickest way to understand the shape of the interface.
Listing an S3 bucket is a plain GET with the service named:
$ awscurl --service s3 'https://awscurl-sample-bucket.s3.amazonaws.com' | tidy -xml -iqThe response is the raw ListBucketResult XML, with the bucket name, the prefix, the marker, the key count and the contents entries. There is no interpretation of that XML, which is the point.
Calling EC2 works the same way, with the action and the API version in the query string:
$ awscurl --service ec2 'https://ec2.amazonaws.com?Action=DescribeRegions&Version=2013-10-15' | tidy -xml -iqAnd calling API Gateway passes a file as the request body to a host whose prefix is the API identifier:
$ awscurl --service execute-api -X POST -d @request.json \
https://<prefix>.execute-api.us-east-1.amazonaws.com/<resource>The `-d @request.json` form is the standard curl convention for reading the body from a file, and it is what makes the tool usable for anything with a non-trivial payload.
The output is piped through tidy in the examples, which is a small signal about the audience: the tool produces XML that you then process with other Unix tools, rather than a parsed structure.
Docker, and the alias that hides the container
The project publishes a Docker image on Docker Hub and on the GitHub container registry, and the README's Docker section is really about convenience rather than deployment.
The straightforward invocation passes the keys explicitly:
$ docker run --rm -it okigan/awscurl --access_key ACCESS_KEY --secret_key SECRET_KEY --service s3 s3://...The alternative mounts the local credentials directory so the container picks up the same profile the host uses:
$ docker run --rm -it -v "$HOME/.aws:/root/.aws" okigan/awscurl --service s3 s3://...Because the second form is what you want most of the time, the README suggests an alias that mounts the credentials and forwards the four relevant environment variables, so that running awscurl behaves as if it were installed on the host:
alias awscurl='docker run --rm -ti -v "$HOME/.aws:/root/.aws" -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e AWS_SECURITY_TOKEN -e AWS_PROFILE okigan/awscurl'The Dockerfile itself is a two-stage build on python:3-alpine, with a builder stage that installs build-base, libffi-dev, libxml2-dev and openssl-dev before installing the package into the user site, and a runtime stage that copies the installed files across and sets the entry point to awscurl.
Mounting ~/.aws into a container is worth a second thought, because that directory holds your credentials in plaintext. The alias is convenient and it is also the point at which you hand a container your keys.
Where the AWS CLI fits instead
The alternative to awscurl is not another signing tool, it is the AWS CLI or boto3, and the difference in approach is about how much of the API you want to understand.
The AWS CLI knows the shape of each service. It knows that a list operation is paginated, it knows the parameter names, it prints a table or JSON that reflects the service's own model, and it has a command for every API operation. A script written against it does not have to construct query strings or interpret XML.
awscurl goes the other way. It has no knowledge of any service, which is exactly why it can call anything the API exposes, including operations that arrived after the CLI was written, and it shows you the request and the response as they really are.
That difference also shows up in the dependency list. awscurl depends on boto3 and botocore, which means it is already using the AWS SDK's signing code, and the same author also publishes awscrt, the AWS common runtime. In other words, the tool takes the SDK's signing and gives you curl's request model.
There is a third option that is neither: doing nothing clever and writing a short script with boto3, which gives you typed calls and paginators without the raw HTTP view. That is the right choice for application code, and awscurl is the right choice when the HTTP layer is what you are trying to see.
Packaging, licensing and where to start
The distribution story is conventional. The package is on PyPI as awscurl, with a version 0.44 in setup.py, an MIT licence, a single package directory named awscurl, and a console script entry point that points at awscurl.__main__:main. The install_requires list covers requests, configargparse, configparser, urllib3, boto3, botocore and awscrt, and there is an extras_require entry for awslibs.
The requirements.txt file is narrower than the install_requires list, listing requests, configargparse, configparser and botocore, which is the set needed for development, with requirements-test.txt alongside it.
There are four install routes in the README, and they cover most situations. The released package is pip install awscurl, the bleeding edge is pip install git+https://github.com/okigan/awscurl, macOS users have brew install awscurl, and the container images cover the rest.
The project also records where it is used in the wild, and the list is a decent argument for the tool's credibility. AWS's own documentation links to it from the guide on calling API Gateway WebSocket APIs, the Onica blog shows it in a Kibana index pattern workflow, and there are questions and answers on StackOverflow and on DevOps StackExchange. The release history shows v0.42 in April 2026, v0.43 and then v0.44 in June 2026, and the last push to the repository was on 2026-10-10.
Editorial conclusion
awscurl suits an engineer who wants to see the exact HTTP request and response an AWS API call produces, who is scripting against an endpoint the AWS CLI does not expose, or who needs to demonstrate a signed request to someone else. It does not suit everyday AWS administration, where the AWS CLI has service-aware commands, pagination and typed output. Before you adopt it, check which credentials your environment actually exposes, because awscurl falls back to ~/.aws/credentials and the default profile when nothing is passed, and read the --service default, which is execute-api rather than a value derived from the URL.
Frequently asked questions
What does awscurl do and what does it not do?
It signs AWS API requests with Signature Version 4 and sends them through a curl-style command line, so you get the raw HTTP request and the raw response. It has no service model, no pagination and no typed output, so it is not a replacement for the AWS CLI or boto3 in everyday use.
How do I install awscurl?
With pip install awscurl for the released package, pip install git+https://github.com/okigan/awscurl for the bleeding edge version, or brew install awscurl on macOS. Container images are published as okigan/awscurl on Docker Hub and ghcr.io/okigan/awscurl on the GitHub registry.
Where do awscurl get its credentials?
From --access_key and --secret_key, or the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables, and otherwise from ~/.aws/credentials. The --profile option or AWS_PROFILE selects a profile, defaulting to default. In general, command-line values override environment variables, which override defaults.
What is the default value of the --service option?
execute-api. The service is not inferred from the URL, so a call to another endpoint has to name it explicitly, for example --service s3, or the request is signed for the wrong service.
How do I run awscurl in Docker with my existing credentials?
Mount the credentials directory into the container, for example docker run --rm -it -v "$HOME/.aws:/root/.aws" okigan/awscurl --service s3 s3://... . The README also suggests an alias that adds the mount and forwards AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SECURITY_TOKEN and AWS_PROFILE so the command behaves as if it were installed locally.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/okigan-awscurl)Was this useful?