{"id":1559,"date":"2025-01-31T07:02:43","date_gmt":"2025-01-31T07:02:43","guid":{"rendered":"https:\/\/mailitics.com\/index.php\/2025\/01\/31\/stop-creating-bad-dags-optimize-your-airflow-environment-by-improving-your-python-code-146fcf4d27f7\/"},"modified":"2025-01-31T07:02:43","modified_gmt":"2025-01-31T07:02:43","slug":"stop-creating-bad-dags-optimize-your-airflow-environment-by-improving-your-python-code-146fcf4d27f7","status":"publish","type":"post","link":"https:\/\/mailitics.com\/index.php\/2025\/01\/31\/stop-creating-bad-dags-optimize-your-airflow-environment-by-improving-your-python-code-146fcf4d27f7\/","title":{"rendered":"Stop Creating Bad DAGs\u200a\u2014\u200aOptimize Your Airflow Environment By Improving Your Python Code"},"content":{"rendered":"<p>    Stop Creating Bad DAGs\u200a\u2014\u200aOptimize Your Airflow Environment By Improving Your Python Code<br \/>\n \t<BR><br \/>\n<BR><\/BR><br \/>\n    <!-- no image --><br \/>\n \t<BR><br \/>\n<BR><\/BR><\/p>\n<div>\n<h3>Stop Creating Bad DAGs\u200a\u2014\u200aOptimize Your Airflow Environment <strong>By Improving Your Python\u00a0Code<\/strong><br \/>\n<\/h3>\n<h4><em>Valuable tips to reduce your DAGs\u2019 parse time and save resources.<\/em><\/h4>\n<figure><img decoding=\"async\" alt=\"\" src=\"https:\/\/cdn-images-1.medium.com\/max\/1024\/0*b1gJwGHK0QyEdTNM\"><figcaption>Photo by <a href=\"https:\/\/unsplash.com\/@danroizer?utm_source=medium&amp;utm_medium=referral\">Dan Roizer<\/a> on\u00a0<a href=\"https:\/\/unsplash.com\/?utm_source=medium&amp;utm_medium=referral\">Unsplash<\/a><\/figcaption><\/figure>\n<p>Apache Airflow is one of the most popular orchestration tools in the data field, powering workflows for companies worldwide. However, anyone who has already worked with Airflow in a production environment, especially in a complex one, knows that it can occasionally present some problems and weird\u00a0bugs.<\/p>\n<p>Among the many aspects you need to manage in an Airflow environment, one critical metric often flies under the radar: <strong>DAG parse time<\/strong>. Monitoring and optimizing parse time is essential to avoid performance bottlenecks and ensure the correct functioning of your orchestrations, as we\u2019ll explore in this\u00a0article.<\/p>\n<p>That said, this tutorial aims to introduce <a href=\"https:\/\/github.com\/AlvaroCavalcante\/airflow-parse-bench\"><strong>airflow-parse-bench<\/strong><\/a>, an open-source tool I developed to help data engineers monitor and optimize their Airflow environments, providing insights to reduce code complexity and parse\u00a0time.<\/p>\n<h3>Why Parse Time\u00a0Matters<\/h3>\n<p>Regarding Airflow, DAG parse time is often an <strong>overlooked metric<\/strong>. Parsing occurs every time Airflow processes your Python files to build the DAGs dynamically.<\/p>\n<p>By default, all your DAGs are parsed every 30 seconds\u200a\u2014\u200aa frequency controlled by the configuration variable <a href=\"https:\/\/airflow.apache.org\/docs\/apache-airflow\/stable\/configurations-ref.html#min-file-process-interval\"><em>min_file_process_interval<\/em><\/a>. This means that every 30 seconds, all the Python code that\u2019s present in your dags folder is read, imported, and processed to generate DAG objects containing the tasks to be scheduled. Successfully processed files are then added to the <a href=\"https:\/\/airflow.apache.org\/docs\/apache-airflow\/stable\/_api\/airflow\/models\/dagbag\/index.html\">DAG\u00a0Bag<\/a>.<\/p>\n<p>Two key Airflow components handle this\u00a0process:<\/p>\n<ul>\n<li>\n<a href=\"https:\/\/airflow.apache.org\/docs\/apache-airflow\/stable\/authoring-and-scheduling\/dagfile-processing.html\"><em>DagFileProcessorManager<\/em><\/a>: Checks which files need to be processed.<\/li>\n<li>\n<a href=\"https:\/\/airflow.apache.org\/docs\/apache-airflow\/stable\/authoring-and-scheduling\/dagfile-processing.html\"><em>DagFileProcessorProcess<\/em><\/a>: Executes the actual file\u00a0parsing.<\/li>\n<\/ul>\n<p>Together, both components (commonly referred to as the <strong>dag processor) <\/strong>are executed by the Airflow <a href=\"https:\/\/airflow.apache.org\/docs\/apache-airflow\/stable\/administration-and-deployment\/scheduler.html\">Scheduler<\/a>, ensuring that your DAG objects are updated before being triggered. However, for scalability and security reasons, it is also possible to run your dag processor as a separate component in your\u00a0cluster.<\/p>\n<p>If your environment only has a few dozen DAGs, it\u2019s unlikely that the parsing process will cause any kind of problem. However, it\u2019s common to find production environments with hundreds or even thousands of DAGs. In this case, if your parse time is too high, it can lead\u00a0to:<\/p>\n<ul>\n<li>Delay DAG scheduling.<\/li>\n<li>Increase resource utilization.<\/li>\n<li>Environment heartbeat issues.<\/li>\n<li>Scheduler failures.<\/li>\n<li>Excessive CPU and memory usage, wasting resources.<\/li>\n<\/ul>\n<blockquote><p>Now, imagine having an environment with hundreds of DAGs containing unnecessarily complex parsing logic. Small inefficiencies can quickly turn into significant problems, affecting the stability and performance of your entire Airflow\u00a0setup.<\/p><\/blockquote>\n<h3>How to write better\u00a0DAGs?<\/h3>\n<p>When writing Airflow DAGs, there are some important best practices to bear in mind to create optimized code. Although you can find a lot of tutorials on how to improve your DAGs, I\u2019ll summarize some of the key principles that can significantly enhance your DAG performance.<\/p>\n<h4>Limit Top-Level Code<\/h4>\n<p>One of the most common causes of high DAG parsing times is inefficient or complex top-level code. Top-level code in an Airflow DAG file is executed every time the Scheduler parses the file. If this code includes resource-intensive operations, such as database queries, API calls, or dynamic task generation, it can significantly impact parsing performance.<\/p>\n<p>The following code shows an example of a <strong>non-optimized DAG<\/strong>:<\/p>\n<p><iframe loading=\"lazy\" src=\"\" width=\"0\" height=\"0\" frameborder=\"0\" scrolling=\"no\"><a href=\"https:\/\/medium.com\/media\/003b2f1bc628053515b19cb24b159bd1\/href\">https:\/\/medium.com\/media\/003b2f1bc628053515b19cb24b159bd1\/href<\/a><\/iframe><\/p>\n<p>In this case, every time the file is parsed by the Scheduler, the top-level code is executed, making an API request and processing the DataFrame, which can significantly impact the parse\u00a0time.<\/p>\n<p>Another important factor contributing to slow parsing is top-level imports. Every library imported at the top level is loaded into memory during parsing, which can be time-consuming. To avoid this, you can move imports into functions or task definitions.<\/p>\n<p>The following code shows a better version of the same\u00a0DAG:<\/p>\n<p><iframe loading=\"lazy\" src=\"\" width=\"0\" height=\"0\" frameborder=\"0\" scrolling=\"no\"><a href=\"https:\/\/medium.com\/media\/5258f82b5ea607bde0a8494fb70717ae\/href\">https:\/\/medium.com\/media\/5258f82b5ea607bde0a8494fb70717ae\/href<\/a><\/iframe><\/p>\n<h4>Avoid Xcoms and Variables in Top-Level Code<\/h4>\n<p>Still talking about the same topic, is particularly interesting to avoid using Xcoms and Variables in your top-level code. As stated by <a href=\"https:\/\/cloud.google.com\/blog\/products\/data-analytics\/reduce-airflow-dag-parse-times-in-cloud-composer\">Google documentation<\/a>:<\/p>\n<blockquote><p>If you are using Variable.get() in top level code, every time the\u00a0.py file is parsed, Airflow executes a Variable.get() which opens a session to the DB. This can dramatically slow down parse\u00a0times.<\/p><\/blockquote>\n<p>To address this, consider using a <strong>JSON dictionary<\/strong> to retrieve multiple variables in a single database query, rather than making multiple Variable.get() calls. Alternatively, use <strong>Jinja templates<\/strong>, as variables retrieved this way are only processed during task execution, not during DAG\u00a0parsing.<\/p>\n<h4>Remove Unnecessary DAGs<\/h4>\n<p>Although it seems obvious, it\u2019s always important to remember to periodically clean up unnecessary DAGs and files from your environment:<\/p>\n<ul>\n<li>\n<strong>Remove unused DAGs<\/strong>: Check your dags folder and delete any files that are no longer\u00a0needed.<\/li>\n<li>\n<strong>Use\u00a0<\/strong><strong>.airflowignore<\/strong>: Specify the files Airflow should intentionally ignore, skipping\u00a0parsing.<\/li>\n<li>\n<strong>Review paused DAGs<\/strong>: Paused DAGs are still parsed by the Scheduler, consuming resources. If they are no longer required, consider removing or archiving them.<\/li>\n<\/ul>\n<h4>Change Airflow Configurations<\/h4>\n<p>Lastly, you could change some Airflow configurations to reduce the Scheduler resource\u00a0usage:<\/p>\n<ul>\n<li>\n<strong>min_file_process_interval<\/strong>: This setting controls how often (in seconds) Airflow parses your DAG files. Increasing it from the default 30 seconds can reduce the Scheduler&#8217;s load at the cost of slower DAG\u00a0updates.<\/li>\n<li>\n<strong>dag_dir_list_interval<\/strong>: This determines how often (in seconds) Airflow scans the dags directory for new DAGs. If you deploy new DAGs infrequently, consider increasing this interval to reduce CPU\u00a0usage.<\/li>\n<\/ul>\n<h3>How to Measure DAG Parse\u00a0Time?<\/h3>\n<p>We\u2019ve discussed a lot about the importance of creating optimized DAGs to maintain a healthy Airflow environment. But how do you actually measure the parse time of your DAGs? Fortunately, there are several ways to do this, depending on your Airflow deployment or operating system.<\/p>\n<p>For example, if you have a <a href=\"https:\/\/cloud.google.com\/composer?hl=en\">Cloud Composer<\/a> deployment, you can easily retrieve a DAG parse report by executing the following command on Google\u00a0CLI:<\/p>\n<pre>gcloud composer environments run $ENVIRONMENT_NAME <br> \u2014 location $LOCATION <br> dags report<\/pre>\n<p>While retrieving parse metrics is straightforward, measuring the effectiveness of your code optimizations can be less so. Every time you modify your code, you need to redeploy the updated Python file to your cloud provider, wait for the DAG to be parsed, and then extract a new report\u200a\u2014\u200aa slow and time-consuming process.<\/p>\n<p>Another possible approach, if you\u2019re on Linux or Mac, is to run this command to measure the parse time locally on your\u00a0machine:<\/p>\n<pre>time python airflow\/example_dags\/example.py<\/pre>\n<p>However, while simple, this approach is not practical for systematically measuring and comparing the parse times of multiple\u00a0DAGs.<\/p>\n<blockquote><p>To address these challenges, I created the <strong>airflow-parse-bench<\/strong>, a Python library that simplifies measuring and comparing the parse times of your DAGs using Airflow&#8217;s native parse\u00a0method.<\/p><\/blockquote>\n<h3>Measuring and Comparing Your DAG\u2019s Parse\u00a0Times<\/h3>\n<p>The <a href=\"https:\/\/github.com\/AlvaroCavalcante\/airflow-parse-bench\"><strong>airflow-parse-bench<\/strong><\/a> tool makes it easy to store parse times, compare results, and standardize comparisons across your\u00a0DAGs.<\/p>\n<h4>Installing the\u00a0Library<\/h4>\n<p>Before installation, it\u2019s recommended to use a <a href=\"https:\/\/docs.python.org\/3\/library\/venv.html\">virtualenv<\/a> to avoid library conflicts. Once set up, you can install the package by running the following command:<\/p>\n<pre>pip install airflow-parse-bench<\/pre>\n<p><strong>Note: <\/strong>This command only installs the essential dependencies (related to Airflow and Airflow providers). You must manually install any additional libraries your DAGs depend\u00a0on.<\/p>\n<p>For example, if a DAG uses boto3 to interact with AWS, ensure that boto3 is installed in your environment. Otherwise, you&#8217;ll encounter parse\u00a0errors.<\/p>\n<p>After that, it&#8217;s necessary to initialize your Airflow database. This can be done by executing the following command:<\/p>\n<pre>airflow db init<\/pre>\n<p>In addition, if your DAGs use <strong>Airflow Variables<\/strong>, you must define them locally as well. However, it\u2019s not necessary to put real values on your variables, as the actual values aren\u2019t required for parsing purposes:<\/p>\n<pre>airflow variables set MY_VARIABLE 'ANY TEST VALUE'<\/pre>\n<p>Without this, you\u2019ll encounter an error\u00a0like:<\/p>\n<pre>error: 'Variable MY_VARIABLE does not exist'<\/pre>\n<h4>Using the\u00a0Tool<\/h4>\n<p>After installing the library, you can begin measuring parse times. For example, suppose you have a DAG file named dag_test.py containing the non-optimized DAG code used in the example\u00a0above.<\/p>\n<p>To measure its parse time, simply\u00a0run:<\/p>\n<pre>airflow-parse-bench --path dag_test.py<\/pre>\n<p>This execution produces the following output:<\/p>\n<figure><img data-recalc-dims=\"1\" decoding=\"async\" alt=\"\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/822\/1%2Al6Vnd2fgtCIDZIWSa2RQYA.png?ssl=1\"><figcaption>Execution result. Image by\u00a0author.<\/figcaption><\/figure>\n<p>As observed, our DAG presented a parse time of <strong>0.61 seconds<\/strong>. If I run the command again, I\u2019ll see some small differences, as parse times can vary slightly across runs due to system and environmental factors:<\/p>\n<figure><img data-recalc-dims=\"1\" decoding=\"async\" alt=\"\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/863\/1%2AWA7Q_uQT2Px-Ys5_t4YY4Q.png?ssl=1\"><figcaption>Result of another execution of the same DAG. Image by\u00a0author.<\/figcaption><\/figure>\n<p>In order to present a more concise number, it\u2019s possible to aggregate multiple executions by specifying the number of iterations:<\/p>\n<pre>airflow-parse-bench --path dag_test.py --num-iterations 5<\/pre>\n<p>Although it takes a bit longer to finish, this calculates the <strong>average parse time<\/strong> across five executions.<\/p>\n<p>Now, to evaluate the impact of the aforementioned optimizations, I replaced the code in mydag_test.py with the optimized version shared earlier. After executing the same command, I got the following result:<\/p>\n<figure><img data-recalc-dims=\"1\" decoding=\"async\" alt=\"\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/865\/1%2ARN-MXORAkcilozdb5m_hVg.png?ssl=1\"><figcaption>Parse result of the optimized code. Image by\u00a0author.<\/figcaption><\/figure>\n<p>As noticed, just applying some good practices was capable of reducing almost <strong>0.5 seconds <\/strong>in the DAG parse time, highlighting the importance of the changes we\u00a0made!<\/p>\n<h3>Further Exploring the\u00a0Tool<\/h3>\n<p>There are other interesting features that I think it\u2019s relevant to\u00a0share.<\/p>\n<p>As a reminder, if you have any doubts or problems using the tool, you can access the complete documentation on\u00a0<a href=\"https:\/\/github.com\/AlvaroCavalcante\/airflow-parse-bench\">GitHub<\/a>.<\/p>\n<p>Besides that, to view all the parameters supported by the library, simply\u00a0run:<\/p>\n<pre>airflow-parse-bench --help<\/pre>\n<h4>Testing Multiple\u00a0DAGs<\/h4>\n<p>In most cases, you likely have dozens of DAGs to test the parse times. To address this use case, I created a folder named dags and put four Python files inside\u00a0it.<\/p>\n<p>To measure the parse times for all the DAGs in a folder, it&#8217;s just necessary to specify the folder path in the &#8211;path parameter:<\/p>\n<pre>airflow-parse-bench --path my_path\/dags<\/pre>\n<p>Running this command produces a table summarizing the parse times for all the DAGs in the\u00a0folder:<\/p>\n<figure><img data-recalc-dims=\"1\" decoding=\"async\" alt=\"\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/1024\/1%2A5EtPoiZWkmSoNYPVEWziNQ.png?ssl=1\"><figcaption>Testing the parse time of multiple DAGs. Image by\u00a0author.<\/figcaption><\/figure>\n<p>By default, the table is sorted from the fastest to the slowest DAG. However, you can reverse the order by using the &#8211;order parameter:<\/p>\n<pre>airflow-parse-bench --path my_path\/dags --order desc<\/pre>\n<figure><img data-recalc-dims=\"1\" decoding=\"async\" alt=\"\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/856\/1%2AKAOl9OIKPP3V9qFafzDyUA.png?ssl=1\"><figcaption>Inverted sorting order. Image by\u00a0author.<\/figcaption><\/figure>\n<h4>Skipping Unchanged DAGs<\/h4>\n<p>The &#8211;skip-unchanged parameter can be especially useful during development. As the name suggests, this option skips the parse execution for DAGs that haven&#8217;t been modified since the last execution:<\/p>\n<pre>airflow-parse-bench --path my_path\/dags --skip-unchanged<\/pre>\n<p>As shown below, when the DAGs remain unchanged, the output reflects no difference in parse\u00a0times:<\/p>\n<figure><img data-recalc-dims=\"1\" decoding=\"async\" alt=\"\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/820\/1%2AA-fL94jEGc00B2j8BbQr0Q.png?ssl=1\"><figcaption>Output with no difference for unchanged files. Image by\u00a0author.<\/figcaption><\/figure>\n<h4>Resetting the\u00a0Database<\/h4>\n<p>All DAG information, including metrics and history, is stored in a local SQLite database. If you want to clear all stored data and start fresh, use the &#8211;reset-db flag:<\/p>\n<pre>airflow-parse-bench --path my_path\/dags --reset-db<\/pre>\n<p>This command resets the database and processes the DAGs as if it were the first execution.<\/p>\n<h3>Conclusion<\/h3>\n<p>Parse time is an important metric for maintaining scalable and efficient Airflow environments, especially as your orchestration requirements become increasingly complex.<\/p>\n<p>For this reason, the <a href=\"https:\/\/github.com\/AlvaroCavalcante\/airflow-parse-bench\"><strong>airflow-parse-bench<\/strong><\/a> library can be an important tool for helping data engineers create better DAGs. By testing your DAGs&#8217; parse time locally, you can easily and quickly find your code bottleneck, making your dags faster and more performant.<\/p>\n<p>Since the code is executed locally, the produced parse time won\u2019t be the same as the one present in your Airflow cluster. However, if you are able to reduce the parse time in your local machine, the same might be reproduced in your cloud environment.<\/p>\n<p>Finally, this project is open for collaboration! If you have suggestions, ideas, or improvements, feel free to contribute on\u00a0<a href=\"https:\/\/github.com\/AlvaroCavalcante\/airflow-parse-bench\">GitHub<\/a>.<\/p>\n<h3>References<\/h3>\n<ul>\n<li><a href=\"https:\/\/cloud.google.com\/blog\/products\/data-analytics\/reduce-airflow-dag-parse-times-in-cloud-composer\">maximize the benefits of Cloud Composer and reduce parse times | Google Cloud Blog<\/a><\/li>\n<li><a href=\"https:\/\/cloud.google.com\/blog\/products\/data-analytics\/optimize-cloud-composer-via-better-airflow-dags\">Optimize Cloud Composer via Better Airflow DAGs | Google Cloud Blog<\/a><\/li>\n<li><a href=\"https:\/\/airflow.apache.org\/docs\/apache-airflow\/stable\/administration-and-deployment\/scheduler.html#fine-tuning-your-scheduler-performance\">Scheduler &#8211; Airflow Documentation<\/a><\/li>\n<li><a href=\"https:\/\/airflow.apache.org\/docs\/apache-airflow\/stable\/best-practices.html#reducing-dag-complexity\">Best Practices &#8211; Airflow Documentation<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/AlvaroCavalcante\/airflow-parse-bench\">GitHub &#8211; AlvaroCavalcante\/airflow-parse-bench: Stop creating bad DAGs! Use this tool to measure and compare the parse time of your DAGs, identify bottlenecks, and optimize your Airflow environment for better performance.<\/a><\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/medium.com\/_\/stat?event=post.clientViewed&amp;referrerSource=full_rss&amp;postId=146fcf4d27f7\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<hr>\n<p><a href=\"https:\/\/towardsdatascience.com\/stop-creating-bad-dags-optimize-your-airflow-environment-by-improving-your-python-code-146fcf4d27f7\">Stop Creating Bad DAGs\u200a\u2014\u200aOptimize Your Airflow Environment By Improving Your Python Code<\/a> was originally published in <a href=\"https:\/\/towardsdatascience.com\/\">Towards Data Science<\/a> on Medium, where people are continuing the conversation by highlighting and responding to this story.<\/p>\n<\/div>\n<p> \t<BR><br \/>\n <BR><\/BR><br \/>\n    Alvaro Leandro Cavalcante Carneiro<br \/>\n \t<BR><br \/>\n<BR><\/BR><br \/>\n<a href=\"https:\/\/medium.com\/m\/global-identity-2?redirectUrl=https%3A%2F%2Ftowardsdatascience.com%2Fstop-creating-bad-dags-optimize-your-airflow-environment-by-improving-your-python-code-146fcf4d27f7\">Go to original source<\/a><br \/>\n \t<BR><br \/>\n <BR><\/BR><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Stop Creating Bad DAGs\u200a\u2014\u200aOptimize Your Airflow Environment By Improving Your Python Code Stop Creating Bad DAGs\u200a\u2014\u200aOptimize Your Airflow Environment By Improving Your Python\u00a0Code Valuable tips to reduce your DAGs\u2019 parse time and save resources. Photo by Dan Roizer on\u00a0Unsplash Apache Airflow is one of the most popular orchestration tools in the data field, powering workflows [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[62,1557,692,401,1558,157],"tags":[1559,1560,163],"class_list":["post-1559","post","type-post","status-publish","format-standard","hentry","category-aimldsaimlds","category-airflow","category-data","category-data-engineering","category-open-source","category-python","tag-airflow","tag-dags","tag-your"],"_links":{"self":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts\/1559"}],"collection":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/comments?post=1559"}],"version-history":[{"count":0,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts\/1559\/revisions"}],"wp:attachment":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/media?parent=1559"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/categories?post=1559"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/tags?post=1559"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}