{"id":3939,"date":"2022-08-10T16:40:44","date_gmt":"2022-08-10T16:40:44","guid":{"rendered":"https:\/\/billsbud.org\/?p=3939"},"modified":"2022-08-10T16:40:44","modified_gmt":"2022-08-10T16:40:44","slug":"new-and-improved-content-moderation-tooling","status":"publish","type":"post","link":"https:\/\/billsbud.org\/?p=3939","title":{"rendered":"New-and-Improved Content Moderation Tooling"},"content":{"rendered":"<p><!--kg-card-begin: markdown--><\/p>\n<div class=\"js-excerpt\">\n<img decoding=\"async\" src=\"https:\/\/openai.com\/content\/images\/2022\/08\/content-moderation-api-1.png\" alt=\"New-and-Improved Content Moderation Tooling\"><\/p>\n<p>We are introducing a new-and-improved content moderation tool: The <a href=\"https:\/\/beta.openai.com\/docs\/api-reference\/moderations\">Moderation endpoint<\/a> improves upon our previous content filter, and is available for free today to OpenAI API developers.<\/p>\n<\/div>\n<p>To help developers protect their applications against possible misuse, we are introducing the faster and more accurate <a href=\"https:\/\/beta.openai.com\/docs\/api-reference\/moderations\">Moderation endpoint<\/a>. This endpoint provides OpenAI API developers with free access to <a href=\"https:\/\/openai.com\/blog\/customized-gpt-3\/\">GPT-based<\/a> classifiers that detect undesired content &#x2014; an instance of <a href=\"https:\/\/openai.com\/blog\/critiques\/\">using AI systems<\/a> to assist with human supervision of these systems. We have also released both a <a href=\"https:\/\/arxiv.org\/abs\/2208.03274\">technical paper<\/a> describing our methodology and the <a href=\"https:\/\/github.com\/openai\/moderation-api-release\">dataset<\/a> used for evaluation.<\/p>\n<p>When given a text input, the Moderation endpoint assesses whether the content is sexual, hateful, violent, or promotes self-harm &#x2014; content prohibited by our <a href=\"https:\/\/beta.openai.com\/docs\/usage-guidelines\/content-policy\">content policy<\/a>. The endpoint has been trained to be quick, accurate, and to perform robustly across a range of applications. Importantly, this reduces the chances of products &#x201C;saying&#x201D; the wrong thing, even when deployed to users at-scale. As a consequence, AI can unlock benefits in sensitive settings, like education, where it could not otherwise be used with confidence.<\/p>\n<div class=\"wide\">\n<div class=\"animation js-animation\">\n<div class=\"animation-inputWrapper\">\n<div class=\"animation-input js-input\">\n        <span class=\"animation-inputLabel\">input text<\/span><\/p>\n<div class=\"animation-inputLinesWrapper\">\n<div class=\"animation-inputLine\"><\/div>\n<div class=\"animation-inputLine\"><\/div>\n<div class=\"animation-inputLine\"><\/div>\n<div class=\"animation-inputLine\"><\/div>\n<\/p><\/div>\n<p>        <svg class=\"animation-inputBorder\" width=\"150\" height=\"80\" fill=\"none\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n          <rect x=\".75\" y=\".75\" width=\"148.5\" height=\"78.5\" rx=\"7.25\" stroke=\"#000\" stroke-width=\"1.5\" stroke-dasharray=\"441.259521484375\" class=\"js-input-outline\"\/>\n          <rect x=\".75\" y=\".75\" width=\"148.5\" height=\"78.5\" rx=\"7.25\" stroke=\"#FF6E3C\" stroke-width=\"1.5\" stroke-dasharray=\"441.259521484375\" style=\"stroke-dashoffset: 441.259521484375;\" class=\"js-input-outline\"\/>\n          <rect x=\".75\" y=\".75\" width=\"148.5\" height=\"78.5\" rx=\"7.25\" stroke=\"#51DA4C\" stroke-width=\"1.5\" stroke-dasharray=\"441.259521484375\" style=\"stroke-dashoffset: 441.259521484375;\" class=\"js-input-outline\"\/>\n        <\/svg>\n      <\/div>\n<\/p><\/div>\n<div class=\"animation-endpoint\">\n<div class=\"animation-endpoint-internals\">\n<div class=\"animation-endpoint-check-type js-endpoint-internals\">Violence<\/div>\n<div class=\"animation-endpoint-check-type js-endpoint-internals\">Self-harm<\/div>\n<div class=\"animation-endpoint-check-type js-endpoint-internals\">Hate<\/div>\n<div class=\"animation-endpoint-check-type js-endpoint-internals\">Sexual<\/div>\n<\/p><\/div>\n<p>      <span class=\"animation-endpoint-name\">Moderation endpoint<\/span>\n    <\/div>\n<p>    <svg class=\"animation-arrow animation-arrow--down js-arrow\" width=\"10\" height=\"43\" viewbox=\"0 0 10 43\" fill=\"none\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n      <path d=\"M5 43L9.33013 35.5H0.669873L5 43ZM4.25 0V36.25H5.75V0H4.25Z\" fill=\"black\"\/>\n    <\/svg><\/p>\n<div class=\"animation-arrow animation-arrow--up js-arrow\">\n      <svg width=\"10\" height=\"43\" viewbox=\"0 0 10 43\" fill=\"none\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n        <path d=\"M5 0L9.33013 7.5H0.669873L5 0ZM4.25 43V6.75H5.75V43H4.25Z\" fill=\"black\"\/>\n      <\/svg><br \/>\n      <span class=\"js-result\">Flagged<\/span>\n    <\/div>\n<p>    <svg width=\"116\" height=\"10\" fill=\"none\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"animation-arrow animation-arrow--right js-arrow\"><path d=\"m115.314 5-7.5-4.33v8.66l7.5-4.33ZM.5 5.75h108.064v-1.5H.5v1.5Z\" fill=\"#000\"\/><\/svg><\/p>\n<div class=\"animation-arrow animation-arrow--left js-arrow\">\n      <svg width=\"116\" height=\"10\" fill=\"none\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\"><path d=\"M.5 5 8 .67v8.66L.5 5Zm114.814.75H7.25v-1.5h108.064v1.5Z\" fill=\"#000\"\/><\/svg><br \/>\n      <span class=\"js-result\">Flagged<\/span>\n    <\/div>\n<\/p><\/div>\n<\/div>\n<p>The Moderation endpoint helps developers to benefit from our infrastructure investments. Rather than build and maintain their own classifiers&#x2014;an extensive process, as we document in our <a href=\"https:\/\/arxiv.org\/abs\/2208.03274\">paper<\/a>&#x2014;they can instead access accurate classifiers through a single API call.<\/p>\n<p>As part of OpenAI&#x2019;s <a href=\"https:\/\/openai.com\/charter\/\">commitment<\/a> to <a href=\"https:\/\/openai.com\/blog\/best-practices-for-deploying-language-models\/\">making the AI ecosystem safer<\/a>, we are providing this endpoint to allow free moderation of all OpenAI API-generated content. For instance, <a href=\"https:\/\/www.inworld.ai\/\">Inworld<\/a>, an OpenAI API customer, uses the Moderation endpoint to help their AI-based virtual characters &#x201C;stay on-script&#x201D;. By leveraging OpenAI&#x2019;s technology, Inworld can focus on their core product &#8211; creating memorable characters.<\/p>\n<p>Additionally,  we welcome the use of the endpoint to moderate content <em>not<\/em> generated with the OpenAI API. In one case, the company <a href=\"https:\/\/ask.fun\/\">NGL<\/a> &#8211; an anonymous messaging platform, with a focus on safety &#8211; uses the Moderation endpoint to detect hateful language and bullying in their application. NGL finds that these classifiers are capable of generalizing to the latest slang, allowing them to remain more-confident over time. Use of the Moderation endpoint to monitor non-API traffic is in private beta and will be subject to a fee. If you are interested, please reach out to us at <a href=\"mailto:support@openai.com\">support@openai.com<\/a>.<\/p>\n<hr>\n<p>Get started with the Moderation endpoint by checking out <a href=\"https:\/\/beta.openai.com\/docs\/guides\/moderation\/overview\">the documentation<\/a>. More details of the training process and model performance are available in our <a href=\"https:\/\/arxiv.org\/abs\/2208.03274\">paper<\/a>. We have also released an <a href=\"https:\/\/github.com\/openai\/moderation-api-release\">evaluation dataset<\/a>, featuring Common Crawl data labeled within these categories, which we hope will spur further research in this area.<\/p>\n<footer class=\"post-footer js-post-footer\">\n<!-- footer item --><\/p>\n<div>\n<hr>\n<div class=\"row\" id=\"acknowledgments\">\n<div class=\"col\">Acknowledgments<\/div>\n<div class=\"col\">Many people reviewed or contributed to this work, to whom we share our thanks, including: Sam Altman, Miles Brundage, Derek Chen, Karl Cobbe, Thomas Degry, Steve Dowling, Elie Georges, Jacob Hilton, Raf Jakubanis, Fraser Kelton, Matt Knight, Gretchen Krueger, Jason Kwon, Jan Leike, Mira Murati, Tinnei Pang, Girish Sastry, Pranav Shyam, Maddie Simens, Natalie Summers, Justin Wang, Peter Welinder, Dave Willner, Hannah Wong, Jeff Wu, and Summer Yue.<\/div>\n<\/p><\/div>\n<\/div>\n<\/footer>\n<style>\n  .animation {\n    width: 100%;\n    max-width: 900px;\n    margin: 0 auto;\n    display: grid;\n    grid-template-rows: repeat(2, auto);\n    font-family: ColfaxAI, Helvetica, sans-serif;\n    font-size: 16px;\n    gap: 120px 0;\n    position: relative;\n  }<\/p>\n<p>  @media (min-width: 900px) {\n    .animation {\n      grid-template-rows: auto;\n      grid-template-columns: repeat(2, 1fr);\n    }\n  }<\/p>\n<p>  .animation-endpoint {\n    aspect-ratio: 1.7 \/ 1;\n    border: 1.5px solid #000;\n    border-radius: 8px;\n    text-align: center;\n    position: relative;  \n  }<\/p>\n<p>  .animation-inputWrapper {\n    display: flex;\n    align-items: center;\n    min-height: 80px;\n  }<\/p>\n<p>  .animation-input {\n    position: relative;\n    border-radius: 8px;\n    width: 150px;\n    padding: 12px;\n    opacity: 0;\n  }<\/p>\n<p>  .animation-inputBorder {\n    position: absolute;\n    top: 0;\n    left: 0;\n    fill: none;\n  }<\/p>\n<p>  .animation-inputLabel {\n    display: block;\n    position: relative;\n    top: -5px;\n    margin-bottom: -2px;\n  }<\/p>\n<p>  .animation-inputLinesWrapper {\n    display: grid;\n    grid-auto-flow: row;\n    grid-auto-rows: 6px;\n    gap: 4px 0;\n    position: relative;\n  }<\/p>\n<p>  .animation-inputLine {\n    background-color: #E5E5E5;\n    border-radius: 4px;\n  }<\/p>\n<p>  .animation-inputLine:nth-child(2) { max-width: 85%; }\n  .animation-inputLine:nth-child(3) { max-width: 90%; }\n  .animation-inputLine:nth-child(4) { max-width: 70%; }<\/p>\n<p>  .animation-endpoint-name {\n    position: absolute;\n    line-height: 1;\n    top: 0;\n    left: 0;\n    transform: translateY(-100%) translateY(-12px);\n  }<\/p>\n<p>  .animation-endpoint-internals {\n    padding: 12px;\n    gap: 12px 12px;\n    display: grid;\n    grid-template-columns: repeat(2, 1fr);\n    height: 100%;\n    position: relative;\n  }<\/p>\n<p>  @media (min-width: 900px) {\n    .animation-endpoint-internals {\n      grid-template-columns: repeat(4, 1fr);\n    }\n  }<\/p>\n<p>  .animation-endpoint-check-type {\n    border: 1.5px solid #000;\n    border-radius: 6px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    opacity: 0.2;\n    transition: all 350ms cubic-bezier(.645, .045, .355, 1);\n  }<\/p>\n<p>  .animation-endpoint-check-type.flagged {\n    border-color: #ff6e3c;\n    color: #ff6e3c;\n  }<\/p>\n<p>  .animation-arrow {\n    position: absolute;\n    opacity: 0;\n    display: flex;\n    flex-direction: row;\n    justify-content: center;\n    align-items: center;\n    text-align: center;\n  }<\/p>\n<p>  .animation-arrow--down {\n    top: 107px;\n    left: 20px;\n  }<\/p>\n<p>  .animation-arrow--up {\n    top: 107px;\n    left: 35px;\n  }<\/p>\n<p>  .animation-arrow--left,\n  .animation-arrow--right {\n    display: none;\n  }<\/p>\n<p>  .animation-arrow--left,\n  .animation-arrow--right {\n    left: 27%;\n    top: calc(50% - 5px);\n    flex-direction: column;\n  }<\/p>\n<p>  .animation-arrow--left {\n    top: calc(50% - 5px + 8px);\n  }<\/p>\n<p>  .animation-arrow--right {\n    top: calc(50% - 5px - 8px);\n  }<\/p>\n<p>  @media (max-width: 899px) {\n    .animation-arrow span {\n      position: relative;\n      top: 4px;\n      margin-left: 8px;\n    }\n  }<\/p>\n<p>  @media (min-width: 900px) {\n    .animation-arrow--up,\n    .animation-arrow--down {\n      display: none;\n    }<\/p>\n<p>    .animation-arrow--left,\n    .animation-arrow--right {\n      display: flex;\n    }\n  }\n<\/style>\n<p><script type=\"module\">\n  const MOBILE_QUERY = \"(max-width: 600px)\";\n  const STROKE_LENGTH = 441.259521484375;<\/p>\n<p>  let isMobile = window.matchMedia(MOBILE_QUERY).matches;\n  let lastResultFlagged = false;\n  let lastFlaggedCategoryIndex = 0;<\/p>\n<p>  const animation = document.querySelector(\".js-animation\");\n  const input = document.querySelector('.js-input');\n  const [downArrow, upArrow, leftArrow, rightArrow] = [...document.querySelectorAll('.js-arrow')];\n  const [defaultOutline, badOutline, goodOutline] = [...document.querySelectorAll('.js-input-outline')];\n  const endPointInternals = [...document.querySelectorAll('.js-endpoint-internals')];\n  const [mobileResultLabel, desktopResultLabel] = [...document.querySelectorAll('.js-result')];<\/p>\n<p>  const handleResize = () => {\n    isMobile = window.matchMedia(MOBILE_QUERY).matches;\n  };<\/p>\n<p>  \/\/ Animation methods\n  const prepare = () => new Promise((resolve) => {\n    [defaultOutline, goodOutline, badOutline].forEach((el, index) => {\n      el.getAnimations({ subtree: true }).forEach((animation) => {\n        animation.cancel();\n      });<\/p>\n<p>      console.log(el.getAnimations({subtree: true}));\n      el.style.strokeDashoffset = index > 0 ? STROKE_LENGTH : 0;\n    });<\/p>\n<p>    resolve();\n  });<\/p>\n<p>  const animateInputIn = () => new Promise((resolve, reject) => {\n    const keyframes = isMobile ? [\n      {transform: 'scale(0.85)', opacity: 0},\n      {opacity: 1, offset: 0.4},\n      {transform: 'scale(1.0)', opacity: 1},\n    ] : [\n      {transform: 'translateY(-30px) scale(0.85)', opacity: 0},\n      {opacity: 1, offset: 0.4},\n      {transform: 'translateY(0) scale(1)', opacity: 1},\n    ];<\/p>\n<p>    const animation = input.animate(\n      keyframes,\n      {fill: 'forwards', duration: 500, easing: 'cubic-bezier(.215, .61, .355, 1)'}\n    );<\/p>\n<p>    animation.onfinish = () => {\n      resolve();\n    }\n  });<\/p>\n<p>  const animateRequest = () => new Promise((resolve, reject) => {\n    const element = isMobile ? downArrow : leftArrow;\n    const keyframes = isMobile ? [\n      {transform: 'translateY(-10px)', opacity: 0},\n      {transform: 'translateX(0)', opacity: 1}\n    ] : [\n      {transform: 'translateX(-20px)', opacity: 0},\n      {transform: 'translateX(0)', opacity: 1}\n    ];<\/p>\n<p>    const animation = element.animate(\n      keyframes,\n      {fill: 'forwards', duration: 300, easing: 'cubic-bezier(.215, .61, .355, 1)'}\n    );<\/p>\n<p>    animation.onfinish = () => {\n      resolve();\n    }\n  });<\/p>\n<p>  const animateInputOut = () => new Promise((resolve) => {\n    const keyframes = isMobile ? [\n      {transform: 'scale(1.0)', opacity: 1},\n      {transform: 'scale(0.85)', opacity: 0},\n    ] : [\n      {transform: 'translateY(0) scale(1)', opacity: 1},\n      {opacity: 0, offset: 0.4},\n      {transform: 'translateY(30px) scale(0.85)', opacity: 0}\n    ]\n    const animation = input.animate(\n      keyframes,\n      {fill: 'forwards', duration: 1000, easing: 'cubic-bezier(.215, .61, .355, 1)'}\n    );<\/p>\n<p>    animation.onfinish = () => {\n      resolve();\n    }\n  });<\/p>\n<p>  const animateInternals = (flag) => {\n    let flaggedFactorIndex = lastFlaggedCategoryIndex;<\/p>\n<p>    if (flag) {\n      while (flaggedFactorIndex === lastFlaggedCategoryIndex) {\n        flaggedFactorIndex = Math.floor(Math.random() * endPointInternals.length);\n      }<\/p>\n<p>      lastFlaggedCategoryIndex = flaggedFactorIndex;\n    }<\/p>\n<p>    return Promise.all(endPointInternals.map((el, index) => new Promise((resolve, reject) => {\n      const isFlagged = flag && index === flaggedFactorIndex;\n      const keyframes = isFlagged\n        ? [{opacity: 0.2}, {opacity: 1.0}, {opacity: 0.8}, {opacity: 1.0}]\n        : [{opacity: 0.2}, {opacity: 1.0}, {opacity: 0.8}, {opacity: 1.0}, {opacity: 0.2}];\n      const animation = el.animate(\n        keyframes,\n        {duration: isFlagged ? 800 : 1000, delay: index * 150, easing: 'cubic-bezier(.645, .045, .355, 1)', fill: 'forwards'},\n      );<\/p>\n<p>      if (isFlagged) {\n        setTimeout(() => {\n          endPointInternals[index].classList.add('flagged');\n        }, 200);\n      }<\/p>\n<p>      animation.onfinish = () => {\n        resolve();\n      }\n    })));\n  }<\/p>\n<p>  const animateResponse = (flag) => new Promise((resolve, reject) => {\n    const element = isMobile ? upArrow : rightArrow;\n    const resultLabel = isMobile ? mobileResultLabel : desktopResultLabel;<\/p>\n<p>    if (flag) {\n      resultLabel.innerText = 'flagged' ;\n      resultLabel.style.color = '#FF6E3C';\n    } else {\n      resultLabel.innerText = 'not flagged' ;\n      resultLabel.style.color = '#51DA4C';\n    }<\/p>\n<p>    const keyframes = isMobile ? [\n      {transform: 'translateY(10px)', opacity: 0},\n      {transform: 'translateY(0)', opacity: 1}\n    ] : [\n      {transform: 'translateX(20px)', opacity: 0},\n      {transform: 'translateX(0)', opacity: 1}\n    ]<\/p>\n<p>    const animation = element.animate(\n      keyframes,\n      {fill: 'forwards', duration: 300, easing: 'cubic-bezier(.215, .61, .355, 1)'}\n    );<\/p>\n<p>    animation.onfinish = () => {\n      resolve();\n    }\n  });<\/p>\n<p>  const animateResult = (flag) => new Promise((resolve, reject) => {\n    const animateOutDefault = defaultOutline.animate(\n      [{strokeDashoffset: 0}, {strokeDashoffset: -1 * STROKE_LENGTH}],\n      {fill: 'forwards', duration: 1400, easing: 'cubic-bezier(.645, .045, .355, 1)'}\n    );<\/p>\n<p>    const resultEl = flag ? badOutline : goodOutline;\n    const animateInResult = resultEl.animate(\n      [{strokeDashoffset: STROKE_LENGTH}, {strokeDashoffset: 0}],\n      {fill: 'forwards', duration: 1400, easing: 'cubic-bezier(.645, .045, .355, 1)'}\n    );<\/p>\n<p>    animateInResult.onfinish = () => {\n      resolve();\n    }\n  });<\/p>\n<p>  const animateArrowsOut = () => new Promise((resolveAll) => {\n    Promise.all([leftArrow, rightArrow, upArrow, downArrow].map((el) => new Promise((resolve) => {\n      const animation = el.animate(\n        [{opacity: 1}, {opacity: 0}],\n        {duration: 500, delay: 1500, fill: 'forwards', easing: 'cubic-bezier(.215, .61, .355, 1)'}\n      );<\/p>\n<p>      animation.onfinish = () => {\n        resolve();\n      }\n    }))).then(() => {\n      resolveAll();\n    });\n  });<\/p>\n<p>  const resetInternals = () => new Promise((resolve) => {\n    endPointInternals.forEach((factor) => {\n      const isFlagged = factor.classList.contains('flagged');<\/p>\n<p>      if (isFlagged) {\n        factor.classList.remove('flagged');<\/p>\n<p>        factor.animate(\n          [{opacity: 1.0}, {opacity: 0.2}],\n          {duration: 500, easing: 'cubic-bezier(.645, .045, .355, 1)', fill: 'forwards'}\n        )\n      }\n    });<\/p>\n<p>    resolve();\n  });<\/p>\n<p>  const cleanUp = () => new Promise((resolve) => {\n    mobileResultLabel.innerText = '';\n    desktopResultLabel.innerText = '';\n    resolve();\n  });<\/p>\n<p>  \/\/ Main logic\n  const ANIMATION_STEPS = [\n    prepare,\n    animateInputIn,\n    animateRequest,\n    animateInternals,\n    animateResponse,\n    animateResult,\n    animateArrowsOut,\n    resetInternals,\n    animateInputOut,\n    cleanUp,\n  ]<\/p>\n<p>  const animate = async () => {\n    const flagResult = !lastResultFlagged;<\/p>\n<p>    for (const animation of ANIMATION_STEPS) {\n      await animation(flagResult);\n    }<\/p>\n<p>    lastResultFlagged = flagResult;<\/p>\n<p>    setTimeout(() => {\n      animate(); \n    }, 500)\n  }<\/p>\n<p>  window.addEventListener('resize', handleResize);\n  window.addEventListener('DOMContentLoaded', () => {\n    animate();\n  });\n<\/script><!--kg-card-end: markdown--><\/p>\n<p>source <a href=\"https:\/\/openai.com\/blog\/new-and-improved-content-moderation-tooling\/\">https:\/\/openai.com\/blog\/new-and-improved-content-moderation-tooling\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>We are introducing a new-and-improved content moderation tool: The Moderation endpoint improves upon our previous content filter, and is available for free today to OpenAI API developers. To help developers protect their applications against possible misuse, we are introducing the faster and more accurate Moderation endpoint. This endpoint provides OpenAI API developers with free access &hellip; <a href=\"https:\/\/billsbud.org\/?p=3939\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">New-and-Improved Content Moderation Tooling<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"nf_dc_page":"","footnotes":""},"categories":[1],"tags":[],"class_list":["post-3939","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/billsbud.org\/index.php?rest_route=\/wp\/v2\/posts\/3939","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/billsbud.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/billsbud.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/billsbud.org\/index.php?rest_route=\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/billsbud.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3939"}],"version-history":[{"count":1,"href":"https:\/\/billsbud.org\/index.php?rest_route=\/wp\/v2\/posts\/3939\/revisions"}],"predecessor-version":[{"id":3940,"href":"https:\/\/billsbud.org\/index.php?rest_route=\/wp\/v2\/posts\/3939\/revisions\/3940"}],"wp:attachment":[{"href":"https:\/\/billsbud.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3939"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/billsbud.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3939"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/billsbud.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3939"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}