{"id":3257,"date":"2025-10-06T13:03:07","date_gmt":"2025-10-06T13:03:07","guid":{"rendered":"https:\/\/www.mjtnet.com\/blog\/?p=3257"},"modified":"2025-10-06T16:44:38","modified_gmt":"2025-10-06T16:44:38","slug":"how-to-call-rest-apis-from-macro-scheduler-with-json-headers-error-handling","status":"publish","type":"post","link":"https:\/\/www.mjtnet.com\/blog\/2025\/10\/06\/how-to-call-rest-apis-from-macro-scheduler-with-json-headers-error-handling\/","title":{"rendered":"How to call REST APIs from Macro Scheduler (with JSON, headers, error handling)"},"content":{"rendered":"\n<p><strong>TL;DR:<\/strong>&nbsp;Use&nbsp;<code>HTTPRequest<\/code>&nbsp;for GET\/POST\/PUT\/PATCH\/DELETE, set&nbsp;<code>HTTP_POSTJSON=1<\/code>&nbsp;for JSON bodies, add&nbsp;<code>HTTP_CUSTOM_HEADERS<\/code>&nbsp;for auth,&nbsp;<code>HTTP_TIMEOUT<\/code>&nbsp;for reliability, and parse responses with&nbsp;<code>JSONParse<\/code>.<\/p>\n\n\n\n<h2>Why call APIs from Macro Scheduler?<\/h2>\n\n\n\n<ul>\n<li>Pull or push data (orders, tickets, metrics) as part of a daily job<\/li>\n\n\n\n<li>Trigger SaaS workflows without opening a browser<\/li>\n\n\n\n<li>Glue apps together (cron-style) with simple, reliable scripts<\/li>\n<\/ul>\n\n\n\n<p>Everything below runs in plain Macro Scheduler\u2014no external dependencies.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2>Quick start: GET a JSON endpoint<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ 1) Optional: set a timeout (seconds)\nLet&gt;HTTP_TIMEOUT=15\n\n\/\/ 2) Optional: custom headers (multiple lines use %CRLF%)\nLet&gt;HTTP_CUSTOM_HEADERS=Accept: application\/json\n\n\/\/ 3) Make the request\nHTTPRequest&gt;https:\/\/api.example.com\/status,,GET,,RESP\n\n\/\/ 4) Parse JSON (example path)\nJSONParse&gt;RESP,$.service,result\nIf&gt;result_count&gt;0\n  MessageModal&gt;Service: %result_1%\nElse\n  \/\/ Log full response if JSON path not found\n  WriteLn&gt;%SCRIPT_DIR%\\api_log.txt,WF_APPEND,Unexpected response: %RESP%\nEndIf\n\nHTTPRequest signature:\nHTTPRequest&gt;URL,&#91;LocalFilename],Method,&#91;POST_Data],Result_Variable mjtnet.com<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2>POST JSON (with Bearer token)<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>Let&gt;HTTP_TIMEOUT=20\nLet&gt;HTTP_POSTJSON=1\nLet&gt;HTTP_CUSTOM_HEADERS=Authorization: Bearer YOUR_API_KEY%CRLF%Accept: application\/json\n\n\/\/ JSON body (can be built up dynamically)\nLet&gt;payload={\"name\":\"Widget 42\",\"price\":19.99}\n\n\/\/ POST it\nHTTPRequest&gt;https:\/\/api.example.com\/items,,POST,payload,RESP\n\n\/\/ Extract a field from the JSON response\nJSONParse&gt;RESP,$.id,itemId\nIf&gt;itemId_count=1\n  MessageModal&gt;Created item id: %itemId_1%\nElse\n  WriteLn&gt;%SCRIPT_DIR%\\api_log.txt,WF_APPEND,Create failed: %RESP%\nEndIf<\/code><\/pre>\n\n\n\n<ul>\n<li>For JSON bodies, set&nbsp;<code>HTTP_POSTJSON=1<\/code>&nbsp;(Macro Scheduler sets the content type for you).&nbsp;<a href=\"https:\/\/www.mjtnet.com\/manuals\/b\/v15\/topics\/httprequest.htm\" target=\"_blank\" rel=\"noreferrer noopener\">mjtnet.com<\/a><\/li>\n\n\n\n<li><code>JSONParse<\/code>&nbsp;returns an array (<code>result_1<\/code>,&nbsp;<code>result_2<\/code>, \u2026) with a&nbsp;<code>*_count<\/code>\u2014use JSONPath like&nbsp;<code>$.id<\/code>.&nbsp;<a href=\"https:\/\/www.mjtnet.com\/manuals\/b\/v15\/topics\/jsonparse.htm\" target=\"_blank\" rel=\"noreferrer noopener\">mjtnet.com<\/a><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2>Custom headers, SSL\/TLS &amp; timeouts (reliability)<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>Let&gt;HTTP_TIMEOUT=30\nLet&gt;HTTP_CUSTOM_HEADERS=Authorization: Bearer YOUR_API_KEY%CRLF%X-Trace: msched<\/code><\/pre>\n\n\n\n<ul>\n<li><strong>Timeouts:<\/strong>&nbsp;set&nbsp;<code>HTTP_TIMEOUT<\/code>&nbsp;(seconds) so a hung API doesn\u2019t block your run.&nbsp;<a href=\"https:\/\/www.mjtnet.com\/manuals\/b\/v15\/topics\/systemvariables.htm\" target=\"_blank\" rel=\"noreferrer noopener\">mjtnet.com<\/a><\/li>\n\n\n\n<li><strong>Headers:<\/strong>&nbsp;set multiple lines via&nbsp;<code>HTTP_CUSTOM_HEADERS<\/code>\u2014great for auth, idempotency keys, etc.&nbsp;<a href=\"https:\/\/www.mjtnet.com\/manuals\/b\/v15\/topics\/httprequest.htm\" target=\"_blank\" rel=\"noreferrer noopener\">mjtnet.com<\/a><\/li>\n\n\n\n<li><strong>HTTPS\/TLS:<\/strong>&nbsp;Use an&nbsp;<code>https:\/\/<\/code>&nbsp;URL (or set&nbsp;<code>HTTP_SSL=1<\/code>). If you must pin a TLS version, set&nbsp;<code>TLS_VER<\/code>&nbsp;(<code>12<\/code>&nbsp;for TLS 1.2).&nbsp;<\/li>\n<\/ul>\n\n\n\n<h2>Example: create a short link with a custom Open Graph preview (ogli)<\/h2>\n\n\n\n<p>Swap in your endpoint\/headers if you\u2019re using another service\u2014the pattern is the same.<\/p>\n\n\n\n<p>This example uses the <a href=\"https:\/\/app.ogli.sh\">Ogli link shorter service<\/a>. Grab a free API key from <a href=\"https:\/\/app.ogli.sh\">Ogli<\/a> to try out the code below.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Go get a free API key from app.ogli.sh\nLet&gt;OGLI_API_KEY=YOUR_API_KEY_HERE\n\n\/\/ Timeouts &amp; JSON\nLet&gt;HTTP_TIMEOUT=20\nLet&gt;HTTP_POSTJSON=1\n\n\/\/ Auth (Bearer) + accept JSON\nLet&gt;HTTP_CUSTOM_HEADERS=Authorization: Bearer %OGLI_API_KEY%%CRLF%Accept: application\/json\n\n\/\/ Build the request body\n\/*\nPAYLOAD:\n{\n\"targetUrl\":\"https:\/\/example.com\/product\/1232\",\n\"title\":\"SuperWidget \u2014 Launch Day\"\n}\n*\/\nLabelToVar&gt;PAYLOAD,postBody\n\n\/\/ POST\nHTTPRequest&gt;https:\/\/api.ogli.sh\/link,,POST,postBody,RESP\n\n\/\/ Parse shortUrl from response\nJSONParse&gt;RESP,$.url,short_link\nIf&gt;short_link_count&gt;0\n  Let&gt;the_link=short_link_1\n  MessageModal&gt;short link is %the_link%\nEndif<\/code><\/pre>\n\n\n\n<p>Later, PATCH the same link to swap the OG image and platforms will refresh the preview when reshared.<\/p>\n\n\n\n<h2>Error handling &amp; logging pattern<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>Let&gt;LOG=%SCRIPT_DIR%\\api_log.txt\n\n\/\/ After any HTTPRequest:\nIf&gt;RESP=\n  \/\/ If you saved to a LocalFileName, RESP will be blank on success.\n  \/\/ Otherwise blank usually means an error occurred \u2013 log it:\n  WriteLn&gt;%LOG%,WF_APPEND,Blank response (check URL\/timeout\/SSL)\nElse\n  \/\/ Try to parse a known field; if none, log full response\n  JSONParse&gt;RESP,$.status,status\n  If&gt;status_count=0\n    WriteLn&gt;%LOG%,WF_APPEND,Unexpected JSON: %RESP%\n  EndIf\nEndIf\n\n<\/code><\/pre>\n\n\n\n<p>Tip: when you&nbsp;<strong>save to a file<\/strong>&nbsp;(use the&nbsp;<code>LocalFilename<\/code>&nbsp;parameter), a successful call leaves the result variable blank by design; otherwise it contains the response or an error message.&nbsp;<a href=\"https:\/\/www.mjtnet.com\/manuals\/b\/v15\/topics\/httprequest.htm\" target=\"_blank\" rel=\"noreferrer noopener\">mjtnet.com<\/a><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2>Parse more JSON with JSONPath<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>\/*\nRESP:\n{\n  \"id\":\"lnk_abc123\",\n  \"shortUrl\":\"https:\/\/ogli.sh\/launch-123\",\n  \"stats\":{\"clicks\":42,\"countries\":&#91;\"GB\",\"US\"]}\n}\n*\/\nJSONParse&gt;RESP,$.stats.clicks,c\nIf&gt;c_count=1\n  Let&gt;clicks=%c_1%\nEndIf\n\n\/\/ Loop an array (countries)\nJSONParse&gt;RESP,$.stats.countries&#91;*],cc\nLet&gt;k=0\nRepeat&gt;k\n  Let&gt;k=k+1\n  Let&gt;country=cc_%k%\n  \/\/ do something with %country%\nUntil&gt;k=cc_count\n\n<\/code><\/pre>\n\n\n\n<p><code>JSONParse<\/code>&nbsp;returns an array and a&nbsp;<code>*_count<\/code>\u2014loop over it with&nbsp;<code>Repeat\/Until<\/code>.&nbsp;<\/p>\n\n\n\n<h2>Common gotchas<\/h2>\n\n\n\n<ul>\n<li><strong>For JSON, always set&nbsp;<code>HTTP_POSTJSON=1<\/code><\/strong>&nbsp;(don\u2019t hand-craft&nbsp;<code>Content-Type<\/code>).&nbsp;<a href=\"https:\/\/www.mjtnet.com\/manuals\/b\/v15\/topics\/httprequest.htm\" target=\"_blank\" rel=\"noreferrer noopener\">mjtnet.com<\/a><\/li>\n\n\n\n<li><strong>Escape quotes<\/strong>&nbsp;inside JSON strings correctly.<\/li>\n\n\n\n<li><strong>Use timeouts<\/strong>&nbsp;so a bad endpoint doesn\u2019t hang your run (<code>Let&gt;HTTP_TIMEOUT=15<\/code>).&nbsp;<a href=\"https:\/\/www.mjtnet.com\/manuals\/b\/v15\/topics\/systemvariables.htm\" target=\"_blank\" rel=\"noreferrer noopener\">mjtnet.com<\/a><\/li>\n\n\n\n<li>If an API returns HTML (error pages),&nbsp;<code>JSONParse<\/code>&nbsp;will fail\u2014<strong>log&nbsp;<code>RESP<\/code><\/strong>&nbsp;to inspect.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2>Further reading<\/h2>\n\n\n\n<ul>\n<li>Macro Scheduler:&nbsp;<code><a href=\"https:\/\/www.mjtnet.com\/manuals\/b\/v15\/topics\/httprequest.htm\">HTTPRequest<\/a><\/code>&nbsp;(methods, headers, files, SSL, timeouts).&nbsp;<a rel=\"noreferrer noopener\" href=\"https:\/\/www.mjtnet.com\/manuals\/b\/v15\/topics\/httprequest.htm\" target=\"_blank\">mjtnet.com<\/a><\/li>\n\n\n\n<li>Macro Scheduler:&nbsp;<code><a href=\"https:\/\/www.mjtnet.com\/manuals\/b\/v15\/topics\/jsonparse.htm\">JSONParse<\/a><\/code>&nbsp;(JSONPath, arrays, examples).&nbsp;<\/li>\n\n\n\n<li>If you prefer you can use Python requests with <a href=\"https:\/\/www.mjtnet.com\/manuals\/b\/v15\/topics\/pyexec.htm\">PyExec<\/a>.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>TL;DR:&nbsp;Use&nbsp;HTTPRequest&nbsp;for GET\/POST\/PUT\/PATCH\/DELETE, set&nbsp;HTTP_POSTJSON=1&nbsp;for JSON bodies, add&nbsp;HTTP_CUSTOM_HEADERS&nbsp;for auth,&nbsp;HTTP_TIMEOUT&nbsp;for reliability, and parse responses with&nbsp;JSONParse. Why call APIs from Macro Scheduler? Everything below runs in plain Macro Scheduler\u2014no external dependencies. Quick start: GET a JSON endpoint POST JSON (with Bearer token) Custom headers, SSL\/TLS &amp; timeouts (reliability) Example: create a short link with a custom Open Graph preview [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/www.mjtnet.com\/blog\/wp-json\/wp\/v2\/posts\/3257"}],"collection":[{"href":"https:\/\/www.mjtnet.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.mjtnet.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.mjtnet.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.mjtnet.com\/blog\/wp-json\/wp\/v2\/comments?post=3257"}],"version-history":[{"count":4,"href":"https:\/\/www.mjtnet.com\/blog\/wp-json\/wp\/v2\/posts\/3257\/revisions"}],"predecessor-version":[{"id":3261,"href":"https:\/\/www.mjtnet.com\/blog\/wp-json\/wp\/v2\/posts\/3257\/revisions\/3261"}],"wp:attachment":[{"href":"https:\/\/www.mjtnet.com\/blog\/wp-json\/wp\/v2\/media?parent=3257"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.mjtnet.com\/blog\/wp-json\/wp\/v2\/categories?post=3257"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.mjtnet.com\/blog\/wp-json\/wp\/v2\/tags?post=3257"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}