Skip to content
HackIndex logo

HackIndex

API Endpoint Enumeration

6 min read Apr 24, 2026

API endpoint enumeration maps the attack surface of REST APIs, GraphQL interfaces, and OpenAPI-documented services. APIs frequently have weaker input validation than frontend forms, expose more functionality than the UI presents, and often lack the same access control enforcement. Run this after content discovery confirms an API path exists, or when fingerprinting reveals a framework that typically exposes APIs.

OpenAPI and Swagger Discovery

Many frameworks auto-generate API documentation at predictable paths. Check these before doing anything else:

┌──(kali㉿kali)-[~]
└─$ for path in /api/docs /api/swagger /swagger /swagger-ui /swagger-ui.html /swagger.json /openapi.json /api/openapi.json /api/v1/docs /docs /redoc /api/redoc; do code=$(curl -skI http://$TARGET_IP:$PORT$path | grep HTTP | awk '{print $2}'); echo "$code $path"; done
404 /api/docs
200 /swagger-ui.html
200 /swagger.json
404 /openapi.json
Explain command
-s Silent mode; suppresses progress meter and error messages.
-k Allows insecure SSL/TLS connections, skipping certificate verification.
-I Sends a HEAD request, fetching only response headers.
$TARGET_IP Placeholder for the target host IP address.
$PORT Placeholder for the target port number.

A 200 on swagger.json or openapi.json gives you the complete API definition including all endpoints, parameters, authentication requirements, and data models. Download it and parse it for targets:

┌──(kali㉿kali)-[~]
└─$ curl -sk http://$TARGET_IP:$PORT/swagger.json | python3 -m json.tool | grep -E '"/(api|v[0-9])'
"/api/v1/users": {
"/api/v1/users/{id}": {
"/api/v1/admin/users": {
"/api/v1/orders": {
Explain command
-s Silent mode; suppresses progress meter and error messages.
-k Allows insecure SSL/TLS connections, skipping certificate verification.
http://$TARGET_IP:$PORT/swagger.json Target URL with variable host and port to fetch the Swagger API spec.
-m json.tool Runs Python's built-in JSON pretty-printer module to format output.
-E Enables extended regular expression syntax in grep pattern matching.
"/(api|v[0-9])' Regex pattern to filter lines containing API or versioned route paths.
┌──(kali㉿kali)-[~]
└─$ curl -sk http://$TARGET_IP:$PORT/swagger.json | python3 -c "import sys,json; spec=json.load(sys.stdin); [print(path) for path in spec.get('paths',{}).keys()]"
/api/v1/users
/api/v1/users/{id}
/api/v1/admin/users
/api/v1/orders
/api/v1/products
Explain command
-s Silent mode; suppresses progress and error output.
-k Allows insecure SSL/TLS connections, skipping cert validation.
http://$TARGET_IP:$PORT/swagger.json Target URL with variable host and port to fetch Swagger API spec.

REST API Endpoint Bruteforce

When no documentation is available, bruteforce common API paths with a dedicated wordlist:

┌──(kali㉿kali)-[~]
└─$ ffuf -w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt -u http://$TARGET_IP:$PORT/api/FUZZ -mc 200,201,204,301,302,401,403 -o api_endpoints.json
Explain command
-w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt Wordlist file path used for fuzzing input.
-u http://$TARGET_IP:$PORT/api/FUZZ Target URL with FUZZ keyword marking the injection point.
$TARGET_IP:$PORT Variable placeholders for the target host IP and port number.
-mc 200,201,204,301,302,401,403 Match only responses with these specific HTTP status codes.
-o api_endpoints.json Write output results to the specified file in JSON format.
┌──(kali㉿kali)-[~]
└─$ ffuf -w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt -u http://$TARGET_IP:$PORT/api/v1/FUZZ -mc 200,201,204,301,302,401,403
Explain command
-w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt Wordlist file path used for fuzzing input.
-u http://$TARGET_IP:$PORT/api/v1/FUZZ Target URL with FUZZ keyword marking the injection point.
$TARGET_IP:$PORT Variable placeholders for the target host IP and port number.
-mc 200,201,204,301,302,401,403 Match only responses with these specific HTTP status codes.

Test common API version prefixes when you don't know the versioning scheme:

┌──(kali㉿kali)-[~]
└─$ for ver in v1 v2 v3 api api/v1 api/v2 rest rest/v1; do code=$(curl -skI http://$TARGET_IP:$PORT/$ver/users | grep HTTP | awk '{print $2}'); echo "$code /$ver/users"; done
404 /v1/users
200 /v2/users
404 /v3/users
401 /api/v1/users
Explain command
-s Silent mode; suppresses progress meter and error messages.
-k Allow insecure TLS connections, skipping certificate verification.
-I Send HTTP HEAD request and show response headers only.
$TARGET_IP:$PORT Target host IP and port number for the HTTP request.

HTTP Method Testing

REST APIs often have different behavior per method. Test GET, POST, PUT, PATCH, and DELETE on discovered endpoints — some may be accessible with methods the application doesn't use in the UI:

┌──(kali㉿kali)-[~]
└─$ for method in GET POST PUT PATCH DELETE OPTIONS; do echo -n "$method: "; curl -sk -X $method http://$TARGET_IP:$PORT/api/v1/users -H 'Content-Type: application/json' -o /dev/null -w '%{http_code}'; echo; done
GET: 200
POST: 201
PUT: 405
PATCH: 403
DELETE: 403
OPTIONS: 200
Explain command
-s Silent mode, suppresses progress meter and error messages.
-k Allow insecure TLS connections, skipping certificate verification.
-X $method Specifies the HTTP method to use for the request.
http://$TARGET_IP:$PORT/api/v1/users Target URL with variable host and port for the API endpoint.
-H 'Content-Type: application/json' Sets the Content-Type request header to application/json.
-o /dev/null Discards the response body by writing output to /dev/null.
-w '%{http_code}' Prints the HTTP response status code after the request completes.

A 403 on DELETE or PATCH where GET returns 200 suggests the endpoint exists but is access-controlled. These are candidates for authorization bypass testing and IDOR testing.

GraphQL Enumeration

GraphQL exposes a single endpoint that handles all queries. Detect it and run introspection to dump the full schema:

┌──(kali㉿kali)-[~]
└─$ for path in /graphql /api/graphql /graphiql /graphql/console /v1/graphql; do code=$(curl -skI http://$TARGET_IP:$PORT$path | grep HTTP | awk '{print $2}'); echo "$code $path"; done
200 /graphql
404 /api/graphql
Explain command
-s Silent mode; suppresses progress meter and error messages.
-k Allow insecure TLS connections, skipping certificate verification.
-I Send HEAD request to fetch HTTP response headers only.
$TARGET_IP Placeholder for the target host IP address.
$PORT Placeholder for the target port number.
/graphql /api/graphql /graphiql /graphql/console /v1/graphql Common GraphQL endpoint paths iterated over in the loop.
┌──(kali㉿kali)-[~]
└─$ curl -sk -X POST http://$TARGET_IP:$PORT/graphql -H 'Content-Type: application/json' -d '{"query":"{__schema{types{name}}}"}' | python3 -m json.tool | grep '"name"'
"name": "Query"
"name": "User"
"name": "Order"
"name": "Admin"
Explain command
-s Silent mode; suppresses progress and error output.
-k Allow insecure SSL connections, skipping certificate verification.
-X POST Specifies HTTP POST as the request method.
http://$TARGET_IP:$PORT/graphql Target GraphQL endpoint using variable host and port.
-H 'Content-Type: application/json' Sets request header to indicate JSON body payload.
-d '{"query":"{__schema{types{name}}}"}' Sends GraphQL introspection query to enumerate schema types.
-m json.tool Pipes output through Python's JSON pretty-printer module.
grep '"name"' Filters output lines containing the 'name' field from schema types.
┌──(kali㉿kali)-[~]
└─$ graphw00f -d -t http://$TARGET_IP:$PORT/graphql
Explain command
-d Enable detection mode to identify the GraphQL engine in use.
-t Specify the target URL to fingerprint.
http://$TARGET_IP:$PORT/graphql Target GraphQL endpoint with variable host and port.

Introspection returning type names confirms GraphQL is active and introspection is enabled. Run a full introspection query to dump all queries, mutations, and field definitions. The presence of an Admin type or mutation is an immediate escalation target.

References