diff --git a/3p/vendors/aso.js b/3p/vendors/aso.js
new file mode 100644
index 000000000000..2cf6b75b2c69
--- /dev/null
+++ b/3p/vendors/aso.js
@@ -0,0 +1,12 @@
+// src/polyfills.js must be the first import.
+import '#3p/polyfills';
+
+import {register} from '#3p/3p';
+import {draw3p, init} from '#3p/integration-lib';
+
+import {aso} from '#ads/vendors/aso';
+
+init(window);
+register('aso', aso);
+
+window.draw3p = draw3p;
diff --git a/ads/_config.js b/ads/_config.js
index eef98ef7f564..c409785e5bcc 100755
--- a/ads/_config.js
+++ b/ads/_config.js
@@ -317,6 +317,10 @@ const adConfig = jsonConfiguration({
renderStartImplemented: true,
},
+ 'aso': {
+ renderStartImplemented: true,
+ },
+
'amoad': {
prefetch: ['https://j.amoad.com/js/a.js', 'https://j.amoad.com/js/n.js'],
preconnect: [
diff --git a/ads/vendors/aso.js b/ads/vendors/aso.js
new file mode 100644
index 000000000000..f6d6166fd482
--- /dev/null
+++ b/ads/vendors/aso.js
@@ -0,0 +1,39 @@
+import {loadScript, validateData} from '#3p/3p';
+
+import {tryParseJson} from '#core/types/object/json';
+
+/**
+ * @param {!Window} global
+ * @param {!Object} data
+ */
+export function aso(global, data) {
+ validateData(data, ['zone'], ['host', 'attr']);
+
+ const host = data.host || 'media.aso1.net';
+
+ global._aso = {
+ onempty: () => global.context.noContentAvailable(),
+ onload: (a) =>
+ global.context.renderStart({
+ width: a.width,
+ height: a.height,
+ }),
+ };
+
+ loadScript(global, `https://${host}/js/code.min.js`, () =>
+ loadAd(global, data)
+ );
+}
+
+/**
+ * @param {!Window} global
+ * @param {!Object} data
+ */
+function loadAd(global, data) {
+ const attr = tryParseJson(data['attr']) || {};
+ attr._amp = 1;
+
+ global._ASO.loadAd('c', data.zone, true, {
+ attr,
+ });
+}
diff --git a/ads/vendors/aso.md b/ads/vendors/aso.md
new file mode 100644
index 000000000000..446c2946d45a
--- /dev/null
+++ b/ads/vendors/aso.md
@@ -0,0 +1,50 @@
+# Adserver.Online
+
+## Example
+
+### Basic
+
+```html
+