HTTP Request
ivy.node.http-request · v0.1.0
ivyx✓
Sends one HTTP request to a URL and returns the status, headers and body, parsed as JSON when the response is JSON. Use it to call any REST API or incoming webhook: a Slack message, a GitHub issue, a CRM contact, a row through a REST layer, your own service. The credential is the token input, written as a Bearer header unless auth_header or auth_scheme says otherwise; reference a stored secret for it, and a URL that is itself a secret (an incoming-webhook URL) can be one too. A 4xx or 5xx answer is an error naming the status and the URL unless fail_on_error is false.
Inputs
| Field | Type | Description |
|---|---|---|
| urlrequired | string | The address to call, starting with http:// or https://. May reference a stored secret when the URL itself is one. |
| method | string | The HTTP method. |
| headers | object | Extra request headers; a value may reference a stored secret. |
| params | object | Query parameters added to the URL. |
| json | Not declared | The JSON body to send; sets Content-Type to application/json. Give json or body, not both. |
| body | string | A raw text body, sent with content_type. Give json or body, not both. |
| content_type | string | The Content-Type of a raw body. |
| token | string | The credential; reference the stored secret instead of writing it. Sent as '<auth_scheme> <token>' in auth_header. |
| auth_header | string | The header the token goes in, for APIs that read X-API-Key or similar. |
| auth_scheme | string | The word before the token; empty sends the token alone. |
| timeout | number | Seconds to wait for the answer. |
| fail_on_error | boolean | Whether a 4xx or 5xx answer is an error. False returns the status and body instead. |
Outputs
| Field | Type | Description |
|---|---|---|
| statusrequired | integer | The HTTP status code. |
| okrequired | boolean | True when the status is below 400. |
| headers | object | The response headers, names in lowercase. |
| json | Not declared | The response body parsed as JSON, or null when it is not JSON. |
| textrequired | string | The response body as text. |
| url | string | The final URL after redirects. |
| elapsed_ms | integer | How long the request took. |
Source
python
inp = __ivy_ctx__["nodes"][__ivy_node_id__]["input"]
import json
import time
import requests
METHODS = ("GET", "POST", "PUT", "PATCH", "DELETE")
url = inp.get("url")
if not isinstance(url, str) or not url.strip():
raise ValueError("url is empty; give the address to call.")
url = url.strip()
if not url.lower().startswith(("http://", "https://")):
raise ValueError(f"url must start with http:// or https://, got {url[:40]!r}.")
method = str(inp.get("method") or "GET").upper()
if method not in METHODS:
raise ValueError(f"method must be one of {', '.join(METHODS)}, got {method!r}.")
headers = {str(k): str(v) for k, v in (inp.get("headers") or {}).items()}
params = inp.get("params") or {}
if not isinstance(params, dict):
raise ValueError("params must be an object of query parameters.")
json_body = inp.get("json")
text_body = inp.get("body")
if json_body is not None and text_body is not None:
raise ValueError("Give json or body, not both.")
token = inp.get("token")
if token:
header_name = str(inp.get("auth_header") or "Authorization")
scheme = inp["auth_scheme"] if "auth_scheme" in inp and inp["auth_scheme"] is not None else "Bearer"
headers[header_name] = (str(scheme).strip() + " " + str(token)).strip()
kwargs = {}
if json_body is not None:
kwargs["json"] = json_body
elif text_body is not None:
kwargs["data"] = str(text_body).encode("utf-8")
headers.setdefault("Content-Type", str(inp.get("content_type") or "text/plain; charset=utf-8"))
timeout = float(inp.get("timeout") or 30)
started = time.time()
try:
response = requests.request(method, url, headers=headers, params=params, timeout=timeout, **kwargs)
except requests.exceptions.Timeout:
raise TimeoutError(f"No answer from {url} within {timeout:g} seconds.")
except requests.exceptions.ConnectionError:
raise ConnectionError(f"Could not reach {url}.")
elapsed_ms = int((time.time() - started) * 1000)
text = response.text
parsed = None
content_type = response.headers.get("Content-Type", "")
if text and ("json" in content_type.lower() or text.lstrip()[:1] in ("{", "[")):
try:
parsed = json.loads(text)
except ValueError:
parsed = None
fail_on_error = inp.get("fail_on_error")
if fail_on_error is None:
fail_on_error = True
if fail_on_error and response.status_code >= 400:
if response.status_code in (401, 403):
raise PermissionError(f"{response.status_code} from {url}: the credentials were refused.")
raise RuntimeError(f"{response.status_code} from {url}: {text[:200]}")
out = __ivy_ctx__["nodes"][__ivy_node_id__]["output"]
out["status"] = response.status_code
out["ok"] = response.status_code < 400
out["headers"] = {k.lower(): v for k, v in response.headers.items()}
out["json"] = parsed
out["text"] = text
out["url"] = response.url
out["elapsed_ms"] = elapsed_msTests
Requires: python:3.9, requests
- get-json
A GET with query parameters comes back with the JSON the server answered.
- post-json
A POST sends its JSON body with the JSON content type.
- bearer
A token is sent as a Bearer header.
- custom-auth-header
An API that reads X-API-Key gets the token there, with no scheme.
- text-body
A raw body goes out as text with the given content type.
- not-found
A 404 is an error that names the status and the address.
- not-found-tolerated
With fail_on_error false the 404 comes back as a status.
- both-bodies
json and body together are refused before any call.