diff --git a/rbx_asset/src/cookie.rs b/rbx_asset/src/cookie.rs
index 54df2ae..f517cdd 100644
--- a/rbx_asset/src/cookie.rs
+++ b/rbx_asset/src/cookie.rs
@@ -95,6 +95,56 @@ impl std::fmt::Display for GetError{
 }
 impl std::error::Error for GetError{}
 
+#[derive(Debug)]
+pub enum GetAssetV2Error{
+	ParseError(url::ParseError),
+	Response(ResponseError),
+	VersionHeaderMissing,
+	ToStr(reqwest::header::ToStrError),
+	ParseInt(std::num::ParseIntError),
+	Reqwest(reqwest::Error),
+}
+impl std::fmt::Display for GetAssetV2Error{
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		write!(f,"{self:?}")
+	}
+}
+impl std::error::Error for GetAssetV2Error{}
+
+#[derive(serde::Deserialize)]
+#[allow(nonstandard_style,dead_code)]
+pub struct GetAssetV2AssetMetadata{
+	pub metadataType:u32,
+	pub value:String,
+}
+#[derive(serde::Deserialize)]
+#[allow(nonstandard_style,dead_code)]
+pub struct GetAssetV2Location{
+	pub assetFormat:String,// "source"
+	location:String,// this value is private so users cannot mutate it
+	pub assetMetadatas:Vec<GetAssetV2AssetMetadata>,
+}
+impl GetAssetV2Location{
+	pub fn location(&self)->&str{
+		&self.location
+	}
+}
+#[derive(serde::Deserialize)]
+#[allow(nonstandard_style,dead_code)]
+pub struct GetAssetV2Info{
+	pub locations:Vec<GetAssetV2Location>,
+	pub requestId:String,
+	pub IsHashDynamic:bool,
+	pub IsCopyrightProtected:bool,
+	pub isArchived:bool,
+	pub assetTypeId:u32,
+}
+
+pub struct GetAssetV2{
+	pub version:u64,
+	pub info:GetAssetV2Info,
+}
+
 #[derive(Debug)]
 #[derive(serde::Deserialize,serde::Serialize)]
 pub enum CreatorType{
@@ -411,6 +461,50 @@ impl CookieContext{
 			Err(e)=>Err(e),
 		}.map_err(GetError::IO)
 	}
+	pub async fn get_asset_v2(&self,config:GetAssetRequest)->Result<GetAssetV2,GetAssetV2Error>{
+		let mut url=reqwest::Url::parse("https://assetdelivery.roblox.com/v2/asset").map_err(GetAssetV2Error::ParseError)?;
+		//url borrow scope
+		{
+			let mut query=url.query_pairs_mut();//borrow here
+			query.append_pair("ID",config.asset_id.to_string().as_str());
+			if let Some(version)=config.version{
+				query.append_pair("version",version.to_string().as_str());
+			}
+		}
+		let response=crate::response_ok(
+			self.get(url).await.map_err(GetAssetV2Error::Reqwest)?
+		).await.map_err(GetAssetV2Error::Response)?;
+
+		let version=response
+			.headers()
+			.get("roblox-assetversionnumber")
+			.ok_or(GetAssetV2Error::VersionHeaderMissing)?
+			.to_str()
+			.map_err(GetAssetV2Error::ToStr)?
+			.parse()
+			.map_err(GetAssetV2Error::ParseInt)?;
+
+		let info=response.json().await.map_err(GetAssetV2Error::Reqwest)?;
+
+		Ok(GetAssetV2{
+			version,
+			info,
+		})
+	}
+	pub async fn get_asset_v2_download(&self,config:&GetAssetV2Location)->Result<Vec<u8>,GetError>{
+		let url=reqwest::Url::parse(config.location.as_str()).map_err(GetError::ParseError)?;
+
+		let body=crate::response_ok(
+			self.get(url).await.map_err(GetError::Reqwest)?
+		).await.map_err(GetError::Response)?
+		.bytes().await.map_err(GetError::Reqwest)?;
+
+		match maybe_gzip_decode(std::io::Cursor::new(body)){
+			Ok(ReaderType::GZip(readable))=>read_readable(readable),
+			Ok(ReaderType::Raw(readable))=>read_readable(readable),
+			Err(e)=>Err(e),
+		}.map_err(GetError::IO)
+	}
 	pub async fn get_asset_details(&self,config:GetAssetDetailsRequest)->Result<AssetDetails,GetError>{
 		let url=reqwest::Url::parse(format!("https://economy.roblox.com/v2/assets/{}/details",config.asset_id).as_str()).map_err(GetError::ParseError)?;
 		crate::response_ok(